mirror of
https://github.com/riptidewave93/UNVR-NAS.git
synced 2025-03-09 15:40:13 +00:00
fix: improvements all around
* add our own fan controller, will need more tuning with time, but it's a great start * add restart/shutdown hooks for ulcmd, so the display shows the state of the system * change how we expose unifi's libs to binaries * Fixup systemd hang at boot due to networking * move ubnthal to systemd task, since we don't load modules due to the unifi initramfs in the prebuilt kernel
This commit is contained in:
parent
459d0a4758
commit
a4f7f862c2
14 changed files with 235 additions and 27 deletions
|
@ -4,7 +4,8 @@ import socket
|
|||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/api/info')
|
||||
|
||||
@app.route("/api/info")
|
||||
def api_info():
|
||||
print(socket.gethostname())
|
||||
payload = {
|
||||
|
@ -13,11 +14,12 @@ def api_info():
|
|||
}
|
||||
return jsonify(payload)
|
||||
|
||||
# No controllers for you
|
||||
@app.route('/api/controllers')
|
||||
def api_controllers():
|
||||
payload = {}
|
||||
return jsonify(payload)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# No controllers for you
|
||||
@app.route("/api/controllers")
|
||||
def api_controllers():
|
||||
return jsonify({})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=11081)
|
||||
|
|
146
overlay/filesystem/usr/bin/unvr-fan-daemon
Executable file
146
overlay/filesystem/usr/bin/unvr-fan-daemon
Executable file
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/python3
|
||||
from functools import lru_cache
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
UBNTHAL_PATH = "/proc/ubnthal/system.info"
|
||||
|
||||
SMARTCTL_PATH = "/usr/sbin/smartctl"
|
||||
|
||||
THERMAL_SYS_PATHS = {
|
||||
"UNVRPRO": {
|
||||
"thermal": [
|
||||
"/sys/devices/virtual/thermal/thermal_zone0/temp",
|
||||
"/sys/class/hwmon/hwmon0/device/temp1_input",
|
||||
"/sys/class/hwmon/hwmon0/device/temp2_input",
|
||||
"/sys/class/hwmon/hwmon0/device/temp3_input",
|
||||
],
|
||||
"fan_pwms": [
|
||||
"/sys/class/hwmon/hwmon0/device/pwm1",
|
||||
"/sys/class/hwmon/hwmon0/device/pwm2",
|
||||
"/sys/class/hwmon/hwmon0/device/pwm3",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@lru_cache(None)
|
||||
def __get_ubnt_device():
|
||||
try:
|
||||
with open(UBNTHAL_PATH, "r") as f:
|
||||
ubnthal_model = [i for i in f.readlines() if i.startswith("shortname=")][0]
|
||||
return ubnthal_model.lstrip("shortname=").rstrip("\n")
|
||||
except FileNotFoundError:
|
||||
print(
|
||||
f"Error: unable to open {UBNTHAL_PATH}; is the ubnthal kernel module loaded?!"
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
def __get_board_temps():
|
||||
# Are we supported?
|
||||
if __get_ubnt_device() not in THERMAL_SYS_PATHS:
|
||||
raise Exception(
|
||||
f"Error: Your Unifi device of {__get_ubnt_device()} is not yet supported by unvr-fan-daemon! Exiting..."
|
||||
)
|
||||
# For each of our paths, get the temps, and append to single return list
|
||||
board_temps = []
|
||||
for path in THERMAL_SYS_PATHS[__get_ubnt_device()]["thermal"]:
|
||||
try:
|
||||
with open(path, "r") as f:
|
||||
board_temps.append(int(f.readline().rstrip("\n")))
|
||||
except FileNotFoundError:
|
||||
print(f"Warning: Unable to open {path}; ignoring and continuing...")
|
||||
continue
|
||||
|
||||
# Did we get ANY temps?!?
|
||||
if len(board_temps) == 0:
|
||||
raise Exception(
|
||||
"Error: Unable to parse out any board temps for your device, something is really wrong! Exiting..."
|
||||
)
|
||||
|
||||
return board_temps
|
||||
|
||||
|
||||
def __get_disk_temps():
|
||||
# Find the list of all devices, which could be none
|
||||
devices = re.findall(
|
||||
r"^[/a-z]+",
|
||||
os.popen(f"{SMARTCTL_PATH} -n standby --scan").read(),
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
# For each disk, get the temp, and append to our list
|
||||
disk_temps = []
|
||||
for dev in devices:
|
||||
dev_temp = re.search(
|
||||
r"^194 [\w-]+\s+0x\d+\s+\d+\s+\d+\s+\d+\s+[\w-]+\s+\w+\s+\S+\s+(\d+)(?:\s[\(][^)]*[\)])?$",
|
||||
os.popen(f"{SMARTCTL_PATH} -A {dev}").read(),
|
||||
re.MULTILINE,
|
||||
)
|
||||
if dev_temp:
|
||||
disk_temps.append(int(f"{dev_temp.group(1)}000")) # Append zeros
|
||||
|
||||
return disk_temps
|
||||
|
||||
|
||||
def __calculate_fan_speed(temp):
|
||||
# our basic fancurve logic
|
||||
match temp:
|
||||
case _ if temp < 40:
|
||||
fanspeed = 25
|
||||
case _ if temp >= 40 and temp < 50:
|
||||
fanspeed = 75
|
||||
case _ if temp >= 50 and temp < 60:
|
||||
fanspeed = 150
|
||||
case _ if temp >= 60 and temp < 70:
|
||||
fanspeed = 200
|
||||
case _:
|
||||
fanspeed = 255
|
||||
|
||||
return fanspeed
|
||||
|
||||
|
||||
def __set_fan_speed(speed: int):
|
||||
# Set the fans
|
||||
for fan in THERMAL_SYS_PATHS[__get_ubnt_device()]["fan_pwms"]:
|
||||
try:
|
||||
with open(fan, "w") as f:
|
||||
f.write(str(speed))
|
||||
except FileNotFoundError:
|
||||
print(
|
||||
f"Error: Unable to write to PWM at {path}! Why can't we set fan speed!?"
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Trigger our model load so it's cached
|
||||
__get_ubnt_device()
|
||||
|
||||
# Cache so we only write to PWMs if this changes
|
||||
last_fanspeed = 0
|
||||
|
||||
print("unvr-fan-daemon starting...")
|
||||
|
||||
# Start with debug write to max speed so we hear it :)
|
||||
__set_fan_speed(255)
|
||||
time.sleep(1)
|
||||
|
||||
# Start our main loop
|
||||
while True:
|
||||
# Get the fanspeed we wanna set based on temps
|
||||
temp = (
|
||||
sorted(__get_board_temps() + __get_disk_temps(), reverse=True)[0] // 1000
|
||||
) # Move temp to C, ignore decimals
|
||||
fanspeed = __calculate_fan_speed(temp)
|
||||
|
||||
# If there's a change in calculated fan speed, set it
|
||||
if last_fanspeed != fanspeed:
|
||||
print(f"Setting fan PWMs to {fanspeed} due to temp of {temp}C")
|
||||
__set_fan_speed(fanspeed)
|
||||
last_fanspeed = fanspeed
|
||||
|
||||
# Sleep and run again
|
||||
time.sleep(5)
|
Loading…
Add table
Add a link
Reference in a new issue