Initial commit

This commit is contained in:
monoadmin
2026-04-10 15:36:36 -07:00
commit 918ff903f0
16 changed files with 929 additions and 0 deletions

323
openwrt/install.sh Executable file
View File

@@ -0,0 +1,323 @@
#!/bin/sh
# Run on the OpenWrt device (20.0.0.26) to install and configure NUT + snmpd
# Also configures: bridge LAN ports, static IP, WiFi AP, DHCP on WiFi only
# Usage: sh /tmp/ups-install.sh
set -e
echo "==> Updating package lists..."
opkg update
echo "==> Installing NUT..."
opkg install nut nut-server nut-upsmon
opkg install nut-driver-usbhid-ups || opkg install nut-usbhid-ups || true
opkg install kmod-usb-hid 2>/dev/null || true
echo "==> Installing LuCI NUT config page and web CGI status..."
opkg install luci-app-nut
echo "==> Installing snmpd (full NET-SNMP with extend support)..."
/etc/init.d/mini_snmpd stop 2>/dev/null || true
/etc/init.d/mini_snmpd disable 2>/dev/null || true
opkg remove mini_snmpd 2>/dev/null || true
opkg install snmpd luci-app-snmpd
echo "==> Configuring NUT via UCI..."
uci set nut_server.tripplite=driver
uci set nut_server.tripplite.driver='usbhid-ups'
uci set nut_server.tripplite.port='auto'
uci set nut_server.tripplite.desc='Local UPS'
uci set nut_server.tripplite.vendorid='09ae'
# No productid filter — matches any Tripp Lite USB UPS (SMART1500RMXL2UA, SU1500RTXL2Ua, etc.)
uci set nut_server.tripplite.pollinterval='5'
uci set nut_server.listen1=listen_address
uci set nut_server.listen1.address='127.0.0.1'
uci set nut_server.upsmon_user=user
uci set nut_server.upsmon_user.username='upsmon'
uci set nut_server.upsmon_user.password='nutlocal'
uci set nut_server.upsmon_user.upsmon='master'
# Read-only passwordless monitor account for CGI stats page
uci set nut_server.monitor_ro=user
uci set nut_server.monitor_ro.username='monitor'
uci set nut_server.monitor_ro.upsmon='slave'
uci commit nut_server
uci set nut_monitor.upsmon=upsmon
uci set nut_monitor.upsmon.minsupplies='1'
uci set nut_monitor.upsmon.shutdowncmd='/sbin/halt'
uci set nut_monitor.upsmon.pollfreq='5'
uci set nut_monitor.upsmon.pollfreqalert='5'
uci set nut_monitor.upsmon.hostsync='15'
uci set nut_monitor.upsmon.deadtime='15'
uci set nut_monitor.upsmon.finaldelay='5'
uci set nut_monitor.tripplite=master
uci set nut_monitor.tripplite.upsname='tripplite'
uci set nut_monitor.tripplite.hostname='localhost'
uci set nut_monitor.tripplite.powervalue='1'
uci set nut_monitor.tripplite.username='upsmon'
uci set nut_monitor.tripplite.password='nutlocal'
uci commit nut_monitor
uci set nut_cgi.tripplite=host
uci set nut_cgi.tripplite.upsname='tripplite'
uci set nut_cgi.tripplite.hostname='localhost'
uci set nut_cgi.tripplite.displayname='Local UPS'
uci commit nut_cgi
echo "==> Configuring snmpd community and UPS extend entries via UCI..."
uci set snmpd.public.community='fsr'
uci set snmpd.public6.community='fsr'
for name in battery_charge battery_runtime ups_load input_voltage output_voltage ups_status battery_temp battery_voltage; do
# Map UCI name back to NUT variable (underscores -> dots, battery_temp -> battery.temperature)
nut_var=$(echo "$name" | tr '_' '.')
[ "$name" = "battery_temp" ] && nut_var="battery.temperature"
uci set snmpd.${name}=extend
uci set snmpd.${name}.name="$name"
uci set snmpd.${name}.prog='/usr/bin/upsc'
uci set snmpd.${name}.args="tripplite@localhost $nut_var"
done
uci commit snmpd
echo "==> Fixing /var/etc/nut directory permissions (persists via rc.local)..."
mkdir -p /var/etc/nut
chgrp nut /var/etc/nut
chmod 750 /var/etc/nut
grep -q 'var/etc/nut' /etc/rc.local 2>/dev/null || \
sed -i 's|^exit 0|mkdir -p /var/etc/nut\nchgrp nut /var/etc/nut\nchmod 750 /var/etc/nut\n\nexit 0|' /etc/rc.local
echo "==> Creating /etc/nut symlinks to UCI-generated configs..."
for f in ups.conf upsd.conf upsd.users nut.conf; do
rm -f /etc/nut/$f
ln -sf /var/etc/nut/$f /etc/nut/$f
done
echo "==> Enabling services..."
/etc/init.d/nut-server enable
/etc/init.d/nut-monitor enable
/etc/init.d/nut-cgi enable
/etc/init.d/snmpd enable
echo "==> Writing USB hotplug script for automatic NUT restart on UPS connect/disconnect..."
cat > /etc/hotplug.d/usb/99-nut-ups << 'HOTPLUG'
#!/bin/sh
# Auto-restart NUT when Tripp Lite UPS is plugged/unplugged
UPS_VENDOR="09ae"
case "$ACTION" in
add)
if echo "$PRODUCT" | grep -qi "^${UPS_VENDOR}"; then
# Fix USB device permissions so NUT driver (nut user) can access it
[ -n "$DEVNAME" ] && chmod 0660 "/dev/$DEVNAME" && chown root:nut "/dev/$DEVNAME"
logger -t NUT "Tripp Lite UPS connected (PRODUCT=$PRODUCT) - restarting NUT"
sleep 3
/etc/init.d/nut-server restart
fi
;;
remove)
if echo "$PRODUCT" | grep -qi "^${UPS_VENDOR}"; then
logger -t NUT "Tripp Lite UPS disconnected - restarting NUT"
/etc/init.d/nut-server restart
fi
;;
esac
HOTPLUG
chmod +x /etc/hotplug.d/usb/99-nut-ups
echo "==> Removing HTTP basic auth from NUT CGI path (upsstats.cgi accessible without login)..."
# nut-web-cgi installs /etc/httpd.conf with auth on /cgi-bin/nut — clear it so upsstats.cgi
# is viewable without a username/password (upsset.cgi still requires upsd credentials)
> /etc/httpd.conf
echo "==> Starting services..."
/etc/init.d/nut-server restart
sleep 10
/etc/init.d/nut-monitor restart
sleep 2
/etc/init.d/nut-cgi restart
/etc/init.d/snmpd restart
echo "==> Configuring network (bridge eth0.1+eth1, static 20.0.0.26/24, WiFi AP 192.168.88.1/24)..."
uci batch << 'EOF'
delete network.wan
delete network.wan6
set network.loopback=interface
set network.loopback.device='lo'
set network.loopback.proto='static'
set network.loopback.ipaddr='127.0.0.1'
set network.loopback.netmask='255.0.0.0'
set network.globals=globals
set network.globals.ula_prefix='fd6b:e811:df87::/48'
set network.br_lan_dev=device
set network.br_lan_dev.name='br-lan'
set network.br_lan_dev.type='bridge'
del_list network.br_lan_dev.ports='eth0.1'
del_list network.br_lan_dev.ports='eth1'
add_list network.br_lan_dev.ports='eth0.1'
add_list network.br_lan_dev.ports='eth1'
set network.lan=interface
set network.lan.device='br-lan'
set network.lan.proto='static'
set network.lan.ipaddr='20.0.0.26'
set network.lan.netmask='255.255.255.0'
set network.lan.gateway='20.0.0.1'
set network.lan.dns='20.0.0.1'
set network.wifi_ap=interface
set network.wifi_ap.proto='static'
set network.wifi_ap.ipaddr='192.168.88.1'
set network.wifi_ap.netmask='255.255.255.0'
EOF
uci commit network
echo "==> Configuring switch VLAN (all wired ports on VLAN 1)..."
uci set network.switch=switch
uci set network.switch.name='switch0'
uci set network.switch.reset='1'
uci set network.switch.enable_vlan='1'
uci set network.switch_vlan1=switch_vlan
uci set network.switch_vlan1.device='switch0'
uci set network.switch_vlan1.vlan='1'
uci set network.switch_vlan1.ports='1 2 3 4 0t'
uci commit network
echo "==> Configuring wireless (SSID: fsr, WPA2, AP on wifi_ap interface)..."
uci set wireless.radio0.disabled='0'
uci set wireless.default_radio0=wifi-iface
uci set wireless.default_radio0.device='radio0'
uci set wireless.default_radio0.network='wifi_ap'
uci set wireless.default_radio0.mode='ap'
uci set wireless.default_radio0.ssid='First Step Internet'
uci set wireless.default_radio0.encryption='psk2'
uci set wireless.default_radio0.key='12345678'
uci commit wireless
echo "==> Configuring DHCP (disabled on lan, enabled on wifi_ap 192.168.88.100-199)..."
uci set dhcp.lan.ignore='1'
uci set dhcp.wifi_ap=dhcp
uci set dhcp.wifi_ap.interface='wifi_ap'
uci set dhcp.wifi_ap.start='100'
uci set dhcp.wifi_ap.limit='100'
uci set dhcp.wifi_ap.leasetime='12h'
uci commit dhcp
echo "==> Writing WiFi control CGI page..."
mkdir -p /www/cgi-bin
cat > /www/cgi-bin/wifi.cgi << 'CGISCRIPT'
#!/bin/sh
urldecode() {
echo -e "$(echo "$1" | sed "s/+/ /g; s/%\([0-9A-Fa-f][0-9A-Fa-f]\)/\\\\x\1/g")"
}
if [ "$REQUEST_METHOD" = "POST" ]; then
read -n "$CONTENT_LENGTH" POST_DATA 2>/dev/null
action=$(echo "$POST_DATA" | tr "&" "\n" | grep "^action=" | cut -d= -f2-)
new_ssid=$(echo "$POST_DATA" | tr "&" "\n" | grep "^ssid=" | cut -d= -f2-)
new_key=$(echo "$POST_DATA" | tr "&" "\n" | grep "^key=" | cut -d= -f2-)
new_ssid=$(urldecode "$new_ssid")
new_key=$(urldecode "$new_key")
case "$action" in
toggle)
disabled=$(uci get wireless.radio0.disabled 2>/dev/null || echo 0)
if [ "$disabled" = "1" ]; then
uci set wireless.radio0.disabled="0"
uci commit wireless
wifi up &
else
uci set wireless.radio0.disabled="1"
uci commit wireless
wifi down &
fi
;;
update)
[ -n "$new_ssid" ] && uci set wireless.default_radio0.ssid="$new_ssid"
[ -n "$new_key" ] && [ ${#new_key} -ge 8 ] && uci set wireless.default_radio0.key="$new_key"
uci commit wireless
wifi reload &
;;
esac
fi
disabled=$(uci get wireless.radio0.disabled 2>/dev/null || echo 0)
ssid=$(uci get wireless.default_radio0.ssid 2>/dev/null)
[ "$disabled" = "1" ] && wstatus="Disabled" && wcolor="#dc2626" && toggle_lbl="Enable WiFi" \
|| wstatus="Enabled" && wcolor="#16a34a" && toggle_lbl="Disable WiFi"
echo "Content-Type: text/html"
echo ""
cat << HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WiFi Control</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#f1f5f9;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
.card{background:#fff;border-radius:12px;box-shadow:0 4px 6px rgba(0,0,0,.1);padding:32px;width:100%;max-width:400px}
h1{font-size:1.3rem;color:#0f172a;margin-bottom:20px}
.status{display:flex;align-items:center;gap:10px;background:#f8fafc;border-radius:8px;padding:12px 16px;margin-bottom:20px}
.dot{width:10px;height:10px;border-radius:50%;background:${wcolor};flex-shrink:0}
.slabel{font-size:.8rem;color:#64748b}
.sval{font-weight:700;color:${wcolor}}
.ssid{font-size:.85rem;color:#475569;margin-top:2px}
.btn{width:100%;padding:11px;border:none;border-radius:8px;font-size:.9rem;font-weight:600;cursor:pointer}
.btn-toggle{background:#0f172a;color:#fff;margin-bottom:20px}
.btn-toggle:hover{background:#1e293b}
.divider{border:none;border-top:1px solid #e2e8f0;margin-bottom:20px}
label{display:block;font-size:.75rem;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px}
input{width:100%;padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:.9rem;margin-bottom:14px;outline:none}
input:focus{border-color:#0284c7;box-shadow:0 0 0 2px rgba(2,132,199,.15)}
.btn-save{background:#0284c7;color:#fff}
.btn-save:hover{background:#0369a1}
.note{font-size:.75rem;color:#94a3b8;margin-top:12px;text-align:center}
</style>
</head>
<body>
<div class="card">
<h1>WiFi Control</h1>
<div class="status">
<div class="dot"></div>
<div>
<div class="slabel">Status: <span class="sval">${wstatus}</span></div>
<div class="ssid">SSID: ${ssid}</div>
</div>
</div>
<form method="POST">
<input type="hidden" name="action" value="toggle">
<button class="btn btn-toggle">${toggle_lbl}</button>
</form>
<hr class="divider">
<form method="POST">
<input type="hidden" name="action" value="update">
<label>SSID</label>
<input type="text" name="ssid" value="${ssid}" placeholder="Network name">
<label>Password <span style="font-weight:400;text-transform:none">(min 8 chars — blank = no change)</span></label>
<input type="password" name="key" placeholder="••••••••">
<button class="btn btn-save">Save &amp; Apply</button>
</form>
<p class="note">Changes apply within ~5 seconds</p>
</div>
</body>
</html>
HTML
CGISCRIPT
chmod +x /www/cgi-bin/wifi.cgi
echo "==> Applying network config (router will be reachable at 20.0.0.26 in ~5s)..."
( sleep 3 && /etc/init.d/network reload && wifi up ) &
echo ""
echo "==> Done!"
echo " UPS status page: http://20.0.0.26/cgi-bin/nut/upsstats.cgi"
echo " WiFi control: http://20.0.0.26/cgi-bin/wifi.cgi"
echo " CLI: upsc tripplite@localhost"
echo " SNMP test: snmpwalk -v2c -c fsr 20.0.0.26 .1.3.6.1.4.1.8072.1.3.2"