Style and polish; add stats readouts and improve elements visuals
This commit is contained in:
parent
ec8f387f0a
commit
336a8ceb02
2 changed files with 187 additions and 23 deletions
71
main.py
71
main.py
|
@ -26,8 +26,8 @@ DATA_SAVE_PATH = DATA_SAVE_FOLDER + DATA_SAVE_FILE
|
||||||
DEFAULT_DATA = {
|
DEFAULT_DATA = {
|
||||||
"version": 0,
|
"version": 0,
|
||||||
"enable": False,
|
"enable": False,
|
||||||
"interpolate": False,
|
"interpolate": True,
|
||||||
"curve": [],
|
"curve": [], # items are {x: int (distance from left), y: int (distance from top, NOT bottom)}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
|
@ -81,11 +81,26 @@ class Plugin:
|
||||||
await self.wait_for_ready(self)
|
await self.wait_for_ready(self)
|
||||||
return self.settings["enable"]
|
return self.settings["enable"]
|
||||||
|
|
||||||
|
async def set_interpol(self, interpolate: bool):
|
||||||
|
await self.wait_for_ready(self)
|
||||||
|
self.settings["interpolate"] = interpolate
|
||||||
|
self.is_changed = True
|
||||||
|
|
||||||
|
async def get_interpol(self) -> bool:
|
||||||
|
await self.wait_for_ready(self)
|
||||||
|
return self.settings["interpolate"]
|
||||||
|
|
||||||
async def set_plot_size(self, x, y):
|
async def set_plot_size(self, x, y):
|
||||||
logging.debug(f"Set plot size to ({x},{y})")
|
logging.debug(f"Set plot size to ({x},{y})")
|
||||||
self.plot_width = x
|
self.plot_width = x
|
||||||
self.plot_height = y
|
self.plot_height = y
|
||||||
|
|
||||||
|
async def get_fan_rpm(self) -> int:
|
||||||
|
return get_fan_input()
|
||||||
|
|
||||||
|
async def get_temperature(self) -> int:
|
||||||
|
return int(thermal_zone(0))
|
||||||
|
|
||||||
async def set_poll_period(self, period):
|
async def set_poll_period(self, period):
|
||||||
self.period_s = period
|
self.period_s = period
|
||||||
|
|
||||||
|
@ -100,21 +115,49 @@ class Plugin:
|
||||||
await asyncio.sleep(0.01)
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
def do_fan_control(self):
|
def do_fan_control(self):
|
||||||
index = -1
|
|
||||||
curve = self.settings["curve"]
|
curve = self.settings["curve"]
|
||||||
temperature = (thermal_zone(0) - TEMPERATURE_MINIMUM) / TEMPERATURE_MAXIMUM
|
fan_ratio = 0 # unnecessary in Python, but stupid without
|
||||||
|
if len(curve) == 0:
|
||||||
|
fan_ratio = 1
|
||||||
|
else:
|
||||||
|
index = -1
|
||||||
|
temperature_ratio = (thermal_zone(0) - TEMPERATURE_MINIMUM) / (TEMPERATURE_MAXIMUM - TEMPERATURE_MINIMUM)
|
||||||
for i in range(len(curve)-1, -1, -1):
|
for i in range(len(curve)-1, -1, -1):
|
||||||
if curve[i]["x"] < temperature:
|
if curve[i]["x"] < temperature_ratio:
|
||||||
index = i
|
index = i
|
||||||
break
|
break
|
||||||
|
if self.settings["interpolate"]:
|
||||||
|
fan_ratio = self.interpolate_fan(self, index, temperature_ratio)
|
||||||
|
else:
|
||||||
|
fan_ratio = self.step_fan(self, index, temperature_ratio)
|
||||||
|
set_fan_target(int((fan_ratio * FAN_MAXIMUM) + FAN_MINIMUM))
|
||||||
|
|
||||||
|
|
||||||
|
def interpolate_fan(self, index, temperature_ratio):
|
||||||
|
curve = self.settings["curve"]
|
||||||
|
upper_point = {"x": 1.0, "y": 0.0}
|
||||||
|
lower_point = {"x": 0.0, "y": 1.0}
|
||||||
|
if index != -1: # guaranteed to not be empty
|
||||||
|
lower_point = curve[index]
|
||||||
|
if index != len(curve) - 1:
|
||||||
|
upper_point = curve[index+1]
|
||||||
|
#logging.debug(f"lower_point: {lower_point}, upper_point: {upper_point}")
|
||||||
|
upper_y = 1-upper_point["y"]
|
||||||
|
lower_y = 1-lower_point["y"]
|
||||||
|
slope_m = (upper_y - lower_y) / (upper_point["x"] - lower_point["x"])
|
||||||
|
y_intercept_b = lower_y - (slope_m * lower_point["x"])
|
||||||
|
logging.debug(f"interpolation: y = {slope_m}x + {y_intercept_b}")
|
||||||
|
return (slope_m * temperature_ratio) + y_intercept_b
|
||||||
|
|
||||||
|
def step_fan(self, index, temperature_ratio):
|
||||||
|
curve = self.settings["curve"]
|
||||||
if index != -1:
|
if index != -1:
|
||||||
target_speed = ((1 - curve[index]["y"]) * FAN_MAXIMUM) + FAN_MINIMUM
|
return 1 - curve[index]["y"]
|
||||||
set_fan_target(int(target_speed))
|
|
||||||
else:
|
else:
|
||||||
if len(curve) == 0:
|
if len(curve) == 0:
|
||||||
set_fan_target(int(FAN_MAXIMUM))
|
return 1
|
||||||
else:
|
else:
|
||||||
set_fan_target(int((FAN_MINIMUM + FAN_MAXIMUM) / 2))
|
return 0.5
|
||||||
|
|
||||||
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||||
async def _main(self):
|
async def _main(self):
|
||||||
|
@ -146,7 +189,7 @@ class Plugin:
|
||||||
self.do_fan_control(self)
|
self.do_fan_control(self)
|
||||||
await asyncio.sleep(self.period_s)
|
await asyncio.sleep(self.period_s)
|
||||||
|
|
||||||
def thermal_zone(index: int):
|
def thermal_zone(index: int) -> float:
|
||||||
with open(f"/sys/class/thermal/thermal_zone{index}/temp", "r") as f:
|
with open(f"/sys/class/thermal/thermal_zone{index}/temp", "r") as f:
|
||||||
result = float(f.read().strip()) / 1000.0
|
result = float(f.read().strip()) / 1000.0
|
||||||
logging.debug(f"Got {result}'C from thermal_zone{index}")
|
logging.debug(f"Got {result}'C from thermal_zone{index}")
|
||||||
|
@ -157,10 +200,16 @@ def set_fan_target(rpm: int):
|
||||||
with open("/sys/class/hwmon/hwmon5/fan1_target", "w") as f:
|
with open("/sys/class/hwmon/hwmon5/fan1_target", "w") as f:
|
||||||
f.write(str(rpm))
|
f.write(str(rpm))
|
||||||
|
|
||||||
|
def get_fan_input() -> int:
|
||||||
|
with open("/sys/class/hwmon/hwmon5/fan1_input", "r") as f:
|
||||||
|
rpm = int(f.read().strip())
|
||||||
|
#logging.debug(f"Got {rpm} from fan1_input") # this is too spammy; runs every 0.5s
|
||||||
|
return rpm
|
||||||
|
|
||||||
def on_enable():
|
def on_enable():
|
||||||
with open("/sys/class/hwmon/hwmon5/recalculate", "w") as f:
|
with open("/sys/class/hwmon/hwmon5/recalculate", "w") as f:
|
||||||
f.write("1")
|
f.write("1")
|
||||||
# TODO disable system fan control
|
# TODO stop system fan control
|
||||||
|
|
||||||
def on_disable():
|
def on_disable():
|
||||||
with open("/sys/class/hwmon/hwmon5/recalculate", "w") as f:
|
with open("/sys/class/hwmon/hwmon5/recalculate", "w") as f:
|
||||||
|
|
131
main_view.html
131
main_view.html
|
@ -6,7 +6,7 @@
|
||||||
<script src="/static/library.js"></script>
|
<script src="/static/library.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const PLOT_HEIGHT = 200;
|
const PLOT_HEIGHT = 200;
|
||||||
const PLOT_WIDTH = 284;
|
const PLOT_WIDTH = 270;
|
||||||
const OFFSET_X = 0;
|
const OFFSET_X = 0;
|
||||||
const OFFSET_Y = 0;
|
const OFFSET_Y = 0;
|
||||||
// state
|
// state
|
||||||
|
@ -47,6 +47,22 @@
|
||||||
return call_plugin_method("get_enable", {});
|
return call_plugin_method("get_enable", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setInterpol(enable) {
|
||||||
|
return call_plugin_method("set_interpol", {"interpolate": enable});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInterpol() {
|
||||||
|
return call_plugin_method("get_interpol", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFanRpm() {
|
||||||
|
return call_plugin_method("get_fan_rpm", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemperature() {
|
||||||
|
return call_plugin_method("get_temperature", {});
|
||||||
|
}
|
||||||
|
|
||||||
function setPlotSize(x, y) {
|
function setPlotSize(x, y) {
|
||||||
return call_plugin_method("set_plot_size", {"x": x, "y": y});
|
return call_plugin_method("set_plot_size", {"x": x, "y": y});
|
||||||
}
|
}
|
||||||
|
@ -54,15 +70,19 @@
|
||||||
// events
|
// events
|
||||||
|
|
||||||
async function onload_body() {
|
async function onload_body() {
|
||||||
|
let hiderDiv = document.getElementById("hiderDiv");
|
||||||
let graphDiv = document.getElementById("graphDiv");
|
let graphDiv = document.getElementById("graphDiv");
|
||||||
await setPlotSize(PLOT_WIDTH, PLOT_HEIGHT);
|
await setPlotSize(PLOT_WIDTH, PLOT_HEIGHT);
|
||||||
const state_controlToggle = await getEnable(); // retrieve from back-end
|
const state_controlToggle = await getEnable(); // retrieve from back-end
|
||||||
setToggleState(document.getElementById("controlToggle"), state_controlToggle);
|
setToggleState(document.getElementById("controlToggle"), state_controlToggle);
|
||||||
showHideElement(graphDiv, state_controlToggle);
|
const state_interpolToggle = await getInterpol(); // retrieve from back-end
|
||||||
|
setToggleState(document.getElementById("interpolToggle"), state_interpolToggle);
|
||||||
|
showHideElement(hiderDiv, state_controlToggle);
|
||||||
if (state_controlToggle) {
|
if (state_controlToggle) {
|
||||||
curve = await getCurve();
|
curve = await getCurve();
|
||||||
buildCurvePlot(curve);
|
buildCurvePlot(curve);
|
||||||
}
|
}
|
||||||
|
window.setInterval(pollStats, 500);
|
||||||
console.log("Loaded");
|
console.log("Loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,16 +99,25 @@
|
||||||
|
|
||||||
async function onclick_controlToggle() {
|
async function onclick_controlToggle() {
|
||||||
console.log("Click @ controlToggle");
|
console.log("Click @ controlToggle");
|
||||||
|
let hiderDiv = document.getElementById("hiderDiv");
|
||||||
let graphDiv = document.getElementById("graphDiv");
|
let graphDiv = document.getElementById("graphDiv");
|
||||||
let controlToggle = document.getElementById("controlToggle");
|
let controlToggle = document.getElementById("controlToggle");
|
||||||
const state_controlToggle = getToggleState(controlToggle);
|
const state_controlToggle = getToggleState(controlToggle);
|
||||||
await setEnable(!state_controlToggle); // notify back-end
|
await setEnable(!state_controlToggle); // notify back-end
|
||||||
setToggleState(controlToggle, !state_controlToggle);
|
setToggleState(controlToggle, !state_controlToggle);
|
||||||
showHideElement(graphDiv, !state_controlToggle);
|
|
||||||
if (!state_controlToggle) {
|
if (!state_controlToggle) {
|
||||||
curve = await getCurve();
|
curve = await getCurve();
|
||||||
buildCurvePlot(curve);
|
buildCurvePlot(curve);
|
||||||
}
|
}
|
||||||
|
showHideElement(hiderDiv, !state_controlToggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onclick_interpolToggle() {
|
||||||
|
console.log("Click @ interpolToggle");
|
||||||
|
let interpolToggle = document.getElementById("interpolToggle");
|
||||||
|
const state_interpolToggle = getToggleState(interpolToggle);
|
||||||
|
await setInterpol(!state_interpolToggle); // notify back-end
|
||||||
|
setToggleState(interpolToggle, !state_interpolToggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onclick_plotPoint(e, index) {
|
async function onclick_plotPoint(e, index) {
|
||||||
|
@ -105,22 +134,49 @@
|
||||||
function buildCurvePlot(curve_points) {
|
function buildCurvePlot(curve_points) {
|
||||||
// TODO
|
// TODO
|
||||||
let graphDiv = document.getElementById("graphDiv");
|
let graphDiv = document.getElementById("graphDiv");
|
||||||
let newStr = "<span style=\"font-size:x-small;position:absolute;left:0px;top:0px;\">100%</span><span style=\"font-size:x-small;position:absolute;left:0px;bottom:0px;\">0</span><span style=\"font-size:x-small;position:absolute;left:0px;bottom:50%;writing-mode:vertical-lr;text-orientation:mixed;\">Fan</span><span style=\"font-size:x-small;position:absolute;right:0px;bottom:0px;\">100</span><span style=\"font-size:x-small;position:absolute;left:35%;bottom:0px;\">Temperature (C)</span>";
|
let newStr = "<span style=\"font-size:x-small;position:absolute;left:1px;top:-1px;\">100%</span><span style=\"font-size:x-small;position:absolute;left:1px;bottom:-1px;\">0</span><span style=\"font-size:x-small;position:absolute;left:-2px;bottom:50%;writing-mode:vertical-lr;text-orientation:mixed;\">Fan</span><span style=\"font-size:x-small;position:absolute;right:1px;bottom:-1px;\">100</span><span style=\"font-size:x-small;position:absolute;left:35%;bottom:-1px;\">Temperature (°C)</span>";
|
||||||
for (let i = 0; i < curve_points.length; i++) {
|
for (let i = 0; i < curve_points.length; i++) {
|
||||||
const point = curve_points[i];
|
const point = curve_points[i];
|
||||||
newStr += "<span style=\"position:absolute;"
|
newStr += "<span style=\"position:absolute;"
|
||||||
newStr += "top:" + Math.round(point["y"]*PLOT_HEIGHT + OFFSET_Y).toString() + "px;left:" + Math.round(point["x"]*PLOT_WIDTH + OFFSET_X).toString() + "px;";
|
newStr += "top:" + Math.round(point["y"]*PLOT_HEIGHT + OFFSET_Y + 1).toString() + "px;left:" + Math.round(point["x"]*PLOT_WIDTH + OFFSET_X + 1).toString() + "px;";
|
||||||
newStr += "width:8px;height:8px;background-color:red;\" id=\"plotPoint";
|
newStr += "width:8px;height:8px;background-color:#1a9fff;border-radius:4px\" id=\"plotPoint";
|
||||||
newStr += i.toString() + "\" onclick=\"onclick_plotPoint(event," + i.toString() + ")\"></span>";
|
newStr += i.toString() + "\" onclick=\"onclick_plotPoint(event," + i.toString() + ")\"></span>";
|
||||||
}
|
}
|
||||||
graphDiv.innerHTML = newStr;
|
graphDiv.innerHTML = newStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pollStats() {
|
||||||
|
/*getFanRpm().then(speed => {
|
||||||
|
let fanNow = document.getElementById("fanNow");
|
||||||
|
fanNow.innerText = speed.toString() + " RPM";
|
||||||
|
});
|
||||||
|
sleep(1).then(_ => {});
|
||||||
|
getTemperature().then(temp => {
|
||||||
|
let tempNow = document.getElementById("tempNow");
|
||||||
|
tempNow.innerText = temp.toString() + " °C";
|
||||||
|
});
|
||||||
|
sleep(1).then(_ => {});*/
|
||||||
|
pollStatsAsync().then(_ => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollStatsAsync() {
|
||||||
|
let fanNow = document.getElementById("fanNow");
|
||||||
|
let tempNow = document.getElementById("tempNow");
|
||||||
|
|
||||||
|
const speed = await getFanRpm();
|
||||||
|
const temp = await getTemperature();
|
||||||
|
|
||||||
|
fanNow.innerText = speed.toString() + " RPM";
|
||||||
|
tempNow.innerText = temp.toString() + " °C";
|
||||||
|
}
|
||||||
|
|
||||||
function showHideElement(elem, visible) {
|
function showHideElement(elem, visible) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
elem.style.visibility = "visible";
|
elem.style.visibility = "visible";
|
||||||
|
elem.style.height = "auto";
|
||||||
} else {
|
} else {
|
||||||
elem.style.visibility = "hidden";
|
elem.style.visibility = "hidden";
|
||||||
|
elem.style.height = "0px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +194,13 @@
|
||||||
function getToggleState(toggle) {
|
function getToggleState(toggle) {
|
||||||
return toggle.classList.contains("gamepaddialog_On_yLrDA");
|
return toggle.classList.contains("gamepaddialog_On_yLrDA");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="onload_body()">
|
<body onload="onload_body()" style="overflow-x:hidden;">
|
||||||
<div class="quickaccessmenu_TabGroupPanel_1QO7b Panel Focusable">
|
<div class="quickaccessmenu_TabGroupPanel_1QO7b Panel Focusable">
|
||||||
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
||||||
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
||||||
|
@ -161,8 +221,63 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="graphDiv" style="height:200px;width:284px;border:2px solid red;position:relative;" onclick="onclick_graphDiv(event)">
|
<div id="hiderDiv">
|
||||||
|
<div id="graphDiv" style="height:200px;width:270px;border:1px solid #1a9fff;position:relative;background-color:#1a1f2c;border-radius:4px;" onclick="onclick_graphDiv(event)">
|
||||||
Some text to show that something is broken :(
|
Some text to show that something is broken :(
|
||||||
</div>
|
</div>
|
||||||
|
<div style="font-size:x-small; text-align:center;">
|
||||||
|
Click to add/remove points on the fan curve.
|
||||||
|
</div>
|
||||||
|
<div class="quickaccessmenu_TabGroupPanel_1QO7b Panel Focusable">
|
||||||
|
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
||||||
|
<div class="quickaccesscontrols_PanelSectionRow_26R5w">
|
||||||
|
<div class="gamepaddialog_Field_eKmEX gamepaddialog_WithFirstRow_2bDqk gamepaddialog_ExtraPaddingOnChildrenBelow_3nLNL gamepaddialog_StandardPadding_xIITX gamepaddialog_HighlightOnFocus_2HFrm Panel Focusable" style="--indent-level:0;">
|
||||||
|
<div class="gamepaddialog_FieldLabelRow_2VcTl">
|
||||||
|
<div class="gamepaddialog_FieldLabel_3jMlJ">
|
||||||
|
Linear Interpolation
|
||||||
|
</div>
|
||||||
|
<div class="gamepaddialog_FieldChildren_2rhav">
|
||||||
|
<div id="interpolToggle" tabindex="0" class="gamepaddialog_Toggle_9Ql-o Focusable" onclick="onclick_interpolToggle()">
|
||||||
|
<div class="gamepaddialog_ToggleRail_2bl0i"></div>
|
||||||
|
<div class="gamepaddialog_ToggleSwitch_1PQpp"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gamepaddialog_FieldDescription_1W1to">Pretends a straight line connects points</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Fan Info -->
|
||||||
|
<div class="quickaccesscontrols_PanelSection_3gY0a" onclick="updateBatteryStats()" style="margin-bottom:0px;">
|
||||||
|
<!--<div class="quickaccesscontrols_PanelSectionTitle_1IigU">
|
||||||
|
<div class="quickaccesscontrols_Text_1cokl">Fan</div>
|
||||||
|
</div>-->
|
||||||
|
<div class="Panel Focusable" tabindex="0">
|
||||||
|
<div class="quickaccesscontrols_PanelSectionRow_3LM_Z">
|
||||||
|
<div class="gamepaddialog_Field_eKmEX gamepaddialog_WithFirstRow_2bDqk gamepaddialog_InlineWrapShiftsChildrenBelow_3LCXh gamepaddialog_StandardPadding_xIITX gamepaddialog_HighlightOnFocus_2HFrm Panel Focusable" style="--indent-level:0;padding-left:0px;padding-right:0px;">
|
||||||
|
<div class="gamepaddialog_FieldLabelRow_2VcTl">
|
||||||
|
<div class="gamepaddialog_FieldLabel_3jMlJ">Current Fan Speed</div>
|
||||||
|
<div class="gamepaddialog_FieldChildren_2rhav">
|
||||||
|
<div class="gamepaddialog_LabelFieldValue_3pteV" id="fanNow"> (|-_-|) </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="Panel Focusable" tabindex="0">
|
||||||
|
<div class="quickaccesscontrols_PanelSectionRow_3LM_Z">
|
||||||
|
<div class="gamepaddialog_Field_eKmEX gamepaddialog_WithFirstRow_2bDqk gamepaddialog_InlineWrapShiftsChildrenBelow_3LCXh gamepaddialog_StandardPadding_xIITX gamepaddialog_HighlightOnFocus_2HFrm Panel Focusable" style="--indent-level:0;padding-left:0px;padding-right:0px;">
|
||||||
|
<div class="gamepaddialog_FieldLabelRow_2VcTl">
|
||||||
|
<div class="gamepaddialog_FieldLabel_3jMlJ">Current Temperature</div>
|
||||||
|
<div class="gamepaddialog_FieldChildren_2rhav">
|
||||||
|
<div class="gamepaddialog_LabelFieldValue_3pteV" id="tempNow"> (|-_-|) </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue