adjustor 3.5.4__tar.gz → 3.6.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. {adjustor-3.5.4/src/adjustor.egg-info → adjustor-3.6.1}/PKG-INFO +1 -1
  2. {adjustor-3.5.4 → adjustor-3.6.1}/pyproject.toml +1 -1
  3. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/const.py +1 -0
  4. adjustor-3.6.1/src/adjustor/core/fan/__init__.py +1 -0
  5. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/fan/alg.py +2 -3
  6. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/fan/core.py +45 -6
  7. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/asus/__init__.py +22 -13
  8. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/general/__init__.py +8 -5
  9. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/smu/__init__.py +123 -5
  10. adjustor-3.6.1/src/adjustor/drivers/smu/qam.yml +80 -0
  11. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/settings.yml +1 -4
  12. {adjustor-3.5.4 → adjustor-3.6.1/src/adjustor.egg-info}/PKG-INFO +1 -1
  13. adjustor-3.5.4/src/adjustor/drivers/__init__.py +0 -0
  14. adjustor-3.5.4/src/adjustor/drivers/smu/qam.yml +0 -21
  15. {adjustor-3.5.4 → adjustor-3.6.1}/LICENSE +0 -0
  16. {adjustor-3.5.4 → adjustor-3.6.1}/MANIFEST.in +0 -0
  17. {adjustor-3.5.4 → adjustor-3.6.1}/readme.md +0 -0
  18. {adjustor-3.5.4 → adjustor-3.6.1}/setup.cfg +0 -0
  19. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/__init__.py +0 -0
  20. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/__main__.py +0 -0
  21. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/__init__.py +0 -0
  22. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/acpi.py +0 -0
  23. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/alib.py +0 -0
  24. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/fan/__main__.py +0 -0
  25. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/fan/utils.py +0 -0
  26. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/lenovo.py +0 -0
  27. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/core/platform.py +0 -0
  28. {adjustor-3.5.4/src/adjustor/core/fan → adjustor-3.6.1/src/adjustor/drivers}/__init__.py +0 -0
  29. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/amd/__init__.py +0 -0
  30. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/amd/power-profiles-daemon.dbus.xml.in +0 -0
  31. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/amd/ppd.py +0 -0
  32. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/amd/settings.yml +0 -0
  33. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/asus/settings.yml +0 -0
  34. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/general/settings.yml +0 -0
  35. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/lenovo/__init__.py +0 -0
  36. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/lenovo/settings.yml +0 -0
  37. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/drivers/smu/smu.yml +0 -0
  38. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/events.py +0 -0
  39. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/fuse/__init__.py +0 -0
  40. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/fuse/driver.py +0 -0
  41. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/fuse/gpu.py +0 -0
  42. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/fuse/utils.py +0 -0
  43. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/hhd.py +0 -0
  44. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor/i18n.py +0 -0
  45. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor.egg-info/SOURCES.txt +0 -0
  46. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor.egg-info/dependency_links.txt +0 -0
  47. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor.egg-info/entry_points.txt +0 -0
  48. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor.egg-info/requires.txt +0 -0
  49. {adjustor-3.5.4 → adjustor-3.6.1}/src/adjustor.egg-info/top_level.txt +0 -0
  50. {adjustor-3.5.4 → adjustor-3.6.1}/usr/share/dbus-1/system.d/hhd-net.hadess.PowerProfiles.conf +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: adjustor
3
- Version: 3.5.4
3
+ Version: 3.6.1
4
4
  Summary: Adjustor, a userspace program for managing the TDP of handheld devices.
5
5
  Author-email: Kapenekakis Antheas <pypi@antheas.dev>
6
6
  Project-URL: Homepage, https://github.com/hhd-dev/adjustor
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adjustor"
3
- version = "3.5.4"
3
+ version = "3.6.1"
4
4
  authors = [
5
5
  { name="Kapenekakis Antheas", email="pypi@antheas.dev" },
6
6
  ]
@@ -120,4 +120,5 @@ CPU_DATA: dict[str, tuple[dict[str, DeviceParams], dict[str, AlibParams]]] = {
120
120
  "AMD Ryzen 7 8840U": (DEV_PARAMS_8040, ALIB_PARAMS_8040),
121
121
  # AMD Athlon Silver 3050e (Win600, will it support tdp?)
122
122
  "AMD Ryzen AI 9 HX 370": (DEV_PARAMS_HX370, ALIB_PARAMS_HX370),
123
+ "AMD Ryzen AI HX 360": (DEV_PARAMS_HX370, ALIB_PARAMS_HX370),
123
124
  }
@@ -0,0 +1 @@
1
+ from .core import get_fan_info, fan_worker
@@ -34,7 +34,6 @@ UPDATE_FREQUENCY = 5
34
34
  UPDATE_T = 1 / UPDATE_FREQUENCY
35
35
  SETPOINT_UPDATE_FREQUENCY = 1
36
36
  SETPOINT_UPDATE_T = 1 / SETPOINT_UPDATE_FREQUENCY
37
- HYSTERESIS_RATIO = 0.25
38
37
 
39
38
 
40
39
  def _calculate_jerk(speed_span, decel_ratio, freq, time):
@@ -133,12 +132,12 @@ def update_setpoint(temp: float, curr: int, fan_curve: dict[int, float]):
133
132
  # Add some hysterisis to avoid dithering
134
133
  if idx > 0:
135
134
  prev = targets[idx - 1]
136
- if temp < prev + (curr - prev) * HYSTERESIS_RATIO:
135
+ if temp < prev:
137
136
  return prev
138
137
 
139
138
  if idx < len(targets) - 1:
140
139
  next = targets[idx + 1]
141
- if temp > next - (next - curr) * HYSTERESIS_RATIO:
140
+ if temp > next:
142
141
  return next
143
142
 
144
143
  return curr
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import time
3
3
  from typing import TypedDict
4
+ from threading import Lock, Event
4
5
 
5
6
  from .alg import (
6
7
  SETPOINT_UPDATE_T,
@@ -40,10 +41,12 @@ class FanState(TypedDict):
40
41
  v_curr: float
41
42
  v_target: float
42
43
  v_target_pwm: int
44
+ t_target: int
43
45
  v_rpm: list[int]
44
46
  t_junction: float
45
47
  t_edge: float
46
48
  fan_data: FanData
49
+ in_setpoint: bool
47
50
 
48
51
 
49
52
  def get_fan_info() -> FanInfo | None:
@@ -131,11 +134,37 @@ def update_fan_speed(
131
134
  "v_rpm": fan_speeds,
132
135
  "t_junction": t_junction,
133
136
  "t_edge": t_edge,
137
+ "t_target": data["t_target"],
134
138
  "fan_data": data,
139
+ "in_setpoint": in_setpoint,
135
140
  },
136
141
  )
137
142
 
138
143
 
144
+ def fan_worker(
145
+ info: FanInfo,
146
+ should_exit: Event,
147
+ lock: Lock,
148
+ fan_curve: dict[int, float],
149
+ state: FanState,
150
+ junction: Event,
151
+ ):
152
+ try:
153
+ set_fans_to_pwm(True, info)
154
+ while not should_exit.is_set():
155
+ with lock:
156
+ state_tmp = state or None # First time state will be empty
157
+ in_setpoint, state_tmp = update_fan_speed(
158
+ state_tmp, info, fan_curve, junction.is_set()
159
+ )
160
+ state.update(state_tmp)
161
+ time.sleep(SETPOINT_UPDATE_T if in_setpoint else UPDATE_T)
162
+ except Exception as e:
163
+ logger.error(f"Fan worker failed:\n{e}")
164
+ finally:
165
+ set_fans_to_pwm(False, info)
166
+
167
+
139
168
  def fan_pwm_tester(normal_curve: bool = True, observe_only: bool = False):
140
169
  fan_info = get_fan_info()
141
170
  if fan_info is None:
@@ -181,12 +210,22 @@ def fan_pwm_tester(normal_curve: bool = True, observe_only: bool = False):
181
210
 
182
211
  state = None
183
212
  for i in range(10000000):
184
- in_setpoint, state = update_fan_speed(state, fan_info, fan_curve, False, observe_only=observe_only)
185
-
186
- print(f"\n> {i:05d}: {'in setpoint' if in_setpoint else 'updating'}{' (observe)' if observe_only else ''}")
187
- print(f" Junction: {state['t_junction']:.2f}C, Edge: {state['t_edge']:.2f}C")
188
- print(f" Current: {state['v_curr']*100:.1f}%, Target: {state['v_target']*100:.1f}%")
189
- print(f" Fan speeds: {' '.join(map(lambda rpm: f"{rpm:4d}rpm/{MAX_FAN}rpm ({100*rpm/MAX_FAN:.1f}%)", state['v_rpm']))}")
213
+ in_setpoint, state = update_fan_speed(
214
+ state, fan_info, fan_curve, False, observe_only=observe_only
215
+ )
216
+
217
+ print(
218
+ f"\n> {i:05d}: {'in setpoint' if in_setpoint else 'updating'}{' (observe)' if observe_only else ''}"
219
+ )
220
+ print(
221
+ f" Junction: {state['t_junction']:.2f}C, Edge: {state['t_edge']:.2f}C"
222
+ )
223
+ print(
224
+ f" Current: {state['v_curr']*100:.1f}%, Target: {state['v_target']*100:.1f}%"
225
+ )
226
+ print(
227
+ f" Fan speeds: {' '.join(map(lambda rpm: f"{rpm:4d}rpm/{MAX_FAN}rpm ({100*rpm/MAX_FAN:.1f}%)", state['v_rpm']))}"
228
+ )
190
229
  time.sleep(SETPOINT_UPDATE_T if in_setpoint else UPDATE_T)
191
230
  except KeyboardInterrupt:
192
231
  print("Exiting fan test.")
@@ -169,7 +169,9 @@ class AsusDriverPlugin(HHDPlugin):
169
169
  path_exists = os.path.exists(EXTREME_FN)
170
170
  extreme_supported = EXTREME_ENABLE and path_exists
171
171
  if self.extreme_supported is None:
172
- logger.info(f"Extreme standby enabled: {EXTREME_ENABLE}, file exists: {extreme_supported}. Enabled: {extreme_supported}")
172
+ logger.info(
173
+ f"Extreme standby enabled: {EXTREME_ENABLE}, file exists: {extreme_supported}. Enabled: {extreme_supported}"
174
+ )
173
175
  self.extreme_supported = extreme_supported
174
176
  if not self.extreme_supported:
175
177
  del out["tdp"]["asus"]["children"]["extreme_standby"]
@@ -177,21 +179,25 @@ class AsusDriverPlugin(HHDPlugin):
177
179
  # Set units
178
180
  if self.allyx:
179
181
  out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["quiet"]["unit"] = "13W"
180
- out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"]["unit"] = "17W"
182
+ out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"][
183
+ "unit"
184
+ ] = "17W"
181
185
  out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["performance"][
182
186
  "unit"
183
187
  ] = "25/30W"
184
188
  else:
185
189
  out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["quiet"]["unit"] = "10W"
186
- out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"]["unit"] = "15W"
190
+ out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["balanced"][
191
+ "unit"
192
+ ] = "15W"
187
193
  out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["performance"][
188
194
  "unit"
189
195
  ] = "25/30W"
190
196
 
191
197
  if not self.enforce_limits:
192
- out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["custom"]["children"]["tdp"][
193
- "max"
194
- ] = 50
198
+ out["tdp"]["asus"]["children"]["tdp_v2"]["modes"]["custom"]["children"][
199
+ "tdp"
200
+ ]["max"] = 50
195
201
  return out
196
202
 
197
203
  def open(
@@ -298,6 +304,7 @@ class AsusDriverPlugin(HHDPlugin):
298
304
  new_target = "performance"
299
305
 
300
306
  # In custom mode, re-apply settings with debounce
307
+ tdp_set = False
301
308
  if mode == "custom":
302
309
  # Check user changed values
303
310
  if new_tdp:
@@ -410,7 +417,7 @@ class AsusDriverPlugin(HHDPlugin):
410
417
 
411
418
  # Check if fan curve has changed
412
419
  # Use debounce logic on these changes
413
- if tdp_reset and manual_fan_curve:
420
+ if ((tdp_reset and mode != "custom") or tdp_set) and manual_fan_curve:
414
421
  self.queue_fan = curr + APPLY_DELAY
415
422
 
416
423
  for i in POINTS:
@@ -431,7 +438,7 @@ class AsusDriverPlugin(HHDPlugin):
431
438
  self.queue_tdp = curr + APPLY_DELAY
432
439
 
433
440
  apply_curve = self.queue_fan and self.queue_fan < curr
434
- if apply_curve:
441
+ if apply_curve or tdp_set:
435
442
  try:
436
443
  if conf["tdp.asus.fan.mode"].to(str) == "manual":
437
444
  set_fan_curve(
@@ -475,7 +482,7 @@ class AsusDriverPlugin(HHDPlugin):
475
482
  with open(EXTREME_FN, "r") as f:
476
483
  cval = f.read().strip() == "1"
477
484
  if nval != cval:
478
- logger.info (f"Setting extreme standby to '{standby}'")
485
+ logger.info(f"Setting extreme standby to '{standby}'")
479
486
  with open(EXTREME_FN, "w") as f:
480
487
  f.write("1" if standby == "enabled" else "0")
481
488
  else:
@@ -487,7 +494,7 @@ class AsusDriverPlugin(HHDPlugin):
487
494
  for ev in events:
488
495
  if ev["type"] == "tdp":
489
496
  self.new_tdp = ev["tdp"]
490
- self.sys_tdp = ev['tdp'] is not None
497
+ self.sys_tdp = ev["tdp"] is not None
491
498
  elif ev["type"] == "ppd":
492
499
  match ev["status"]:
493
500
  case "power":
@@ -496,10 +503,12 @@ class AsusDriverPlugin(HHDPlugin):
496
503
  self.new_mode = "balanced"
497
504
  case "performance":
498
505
  self.new_mode = "performance"
499
- elif ev['type'] == 'special' and ev.get('event', None) == "wakeup":
500
- logger.info(f"Waking up from sleep, resetting TDP after {SLEEP_DELAY} seconds.")
506
+ elif ev["type"] == "special" and ev.get("event", None) == "wakeup":
507
+ logger.info(
508
+ f"Waking up from sleep, resetting TDP after {SLEEP_DELAY} seconds."
509
+ )
501
510
  self.queue_tdp = time.time() + SLEEP_DELAY
502
- elif self.cycle_tdp and ev['type'] == "special" and ev['event'] == "xbox_y":
511
+ elif self.cycle_tdp and ev["type"] == "special" and ev["event"] == "xbox_y":
503
512
  match self.mode:
504
513
  case "quiet":
505
514
  self.new_mode = "balanced"
@@ -32,6 +32,7 @@ class GeneralPowerPlugin(HHDPlugin):
32
32
  self.ovr_enabled = False
33
33
  self.should_exit = Event()
34
34
  self.t_sys = None
35
+ self.currentTarget = None
35
36
 
36
37
  def settings(self):
37
38
  sets = load_relative_yaml("./settings.yml")
@@ -151,10 +152,11 @@ class GeneralPowerPlugin(HHDPlugin):
151
152
  "balanced": "balanced",
152
153
  "performance": "throughput-performance"
153
154
  }
155
+
154
156
  new_profile = ppd_tuned_mapping.get(conf.get("tdp.general.profile", self.target))
155
- if new_profile != self.target and new_profile and self.target:
156
- logger.info(f"Setting TuneD profile to '{new_profile}'")
157
- self.target = new_profile
157
+ if new_profile != self.currentTarget and new_profile and self.currentTarget:
158
+ logger.info(f"Setting TuneD profile to '{new_profile}' from '{self.target}'")
159
+ self.currentTarget = new_profile
158
160
  try:
159
161
  subprocess.run(
160
162
  [shutil.which('tuned-adm'), "profile", new_profile],
@@ -162,6 +164,7 @@ class GeneralPowerPlugin(HHDPlugin):
162
164
  stdout=subprocess.PIPE,
163
165
  stderr=subprocess.PIPE,
164
166
  )
167
+
165
168
  except Exception as e:
166
169
  self.tuned_supported = False
167
170
  logger.warning(f"tuned-adm returned with error:\n{e}")
@@ -176,13 +179,13 @@ class GeneralPowerPlugin(HHDPlugin):
176
179
  stdout=subprocess.PIPE,
177
180
  stderr=subprocess.PIPE,
178
181
  )
179
-
180
182
  tuned_ppd_mapping = {
181
183
  "powersave": "power-saver",
182
184
  "balanced": "balanced",
183
185
  "throughput-performance": "performance"
184
186
  }
185
- self.target = tuned_ppd_mapping.get(res.stdout.decode().split(":")[1].strip()) # type: ignore
187
+ self.currentTarget = res.stdout.decode().split(":")[1].strip()
188
+ self.target = tuned_ppd_mapping.get(self.currentTarget) # type: ignore
186
189
 
187
190
  if self.target != conf["tdp.general.profile"].to(str):
188
191
  conf["tdp.general.profile"] = self.target
@@ -1,11 +1,13 @@
1
1
  import logging
2
2
  import time
3
+ from threading import Event as TEvent, Lock, Thread
3
4
  from typing import Sequence
4
5
 
5
6
  from hhd.plugins import Context, Event, HHDPlugin, load_relative_yaml
6
7
  from hhd.plugins.conf import Config
7
8
 
8
9
  from adjustor.core.alib import AlibParams, DeviceParams, alib
10
+ from adjustor.core.fan import fan_worker, get_fan_info
9
11
  from adjustor.core.platform import get_platform_choices, set_platform_profile
10
12
  from adjustor.i18n import _
11
13
 
@@ -15,6 +17,27 @@ PP_DELAY = 0.2
15
17
  APPLY_DELAY = 1
16
18
  SLEEP_DELAY = 4
17
19
 
20
+ DEFAULT_EDGE = {
21
+ 40: 25,
22
+ 45: 25,
23
+ 50: 40,
24
+ 55: 45,
25
+ 60: 50,
26
+ 65: 55,
27
+ 70: 70,
28
+ 80: 85,
29
+ 90: 100,
30
+ }
31
+ DEFAULT_TCTL = {
32
+ 40: 40,
33
+ 50: 45,
34
+ 60: 50,
35
+ 70: 80,
36
+ 80: 90,
37
+ 90: 100,
38
+ 100: 100,
39
+ }
40
+
18
41
 
19
42
  class SmuQamPlugin(HHDPlugin):
20
43
 
@@ -44,6 +67,14 @@ class SmuQamPlugin(HHDPlugin):
44
67
  self.is_set = False
45
68
  self.lims = self.dev.get("skin_limit", self.dev.get("stapm_limit", None))
46
69
 
70
+ self.fan_info = None
71
+ self.fan_t = None
72
+ self.fan_should_exit = TEvent()
73
+ self.fan_junction = TEvent()
74
+ self.fan_lock = Lock()
75
+ self.fan_curve = {}
76
+ self.fan_state = {}
77
+
47
78
  # Workaround for debugging on the legion go
48
79
  # Avoids sending SMU commands that will conflict with Lenovo TDP on
49
80
  # startup
@@ -87,6 +118,33 @@ class SmuQamPlugin(HHDPlugin):
87
118
  {"min": dmin, "max": dmax, "default": default}
88
119
  )
89
120
 
121
+ if not self.fan_info:
122
+ del out["tdp"]["qam"]["children"]["fan"]
123
+ else:
124
+ base = out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_edge"][
125
+ "children"
126
+ ]["st40"]
127
+ reset = out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_edge"][
128
+ "children"
129
+ ].pop("reset")
130
+ for k, v in DEFAULT_EDGE.items():
131
+ out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_edge"][
132
+ "children"
133
+ ][f"st{k}"] = {**base, "title": f"{k}C", "default": v}
134
+ out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_edge"]["children"][
135
+ "reset"
136
+ ] = reset
137
+ reset = out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_junction"][
138
+ "children"
139
+ ].pop("reset")
140
+ for k, v in DEFAULT_TCTL.items():
141
+ out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_junction"][
142
+ "children"
143
+ ][f"st{k}"] = {**base, "title": f"{k}C", "default": v}
144
+ out["tdp"]["qam"]["children"]["fan"]["modes"]["manual_junction"][
145
+ "children"
146
+ ]["reset"] = reset
147
+
90
148
  return out
91
149
 
92
150
  def open(
@@ -95,6 +153,7 @@ class SmuQamPlugin(HHDPlugin):
95
153
  context: Context,
96
154
  ):
97
155
  self.emit = emit
156
+ self.fan_info = get_fan_info()
98
157
 
99
158
  def update(self, conf: Config):
100
159
  self.enabled = conf["hhd.settings.tdp_enable"].to(bool)
@@ -191,12 +250,65 @@ class SmuQamPlugin(HHDPlugin):
191
250
  self.old_tdp = new_tdp
192
251
  self.old_boost = new_boost
193
252
 
253
+ if self.fan_info:
254
+ mode = conf["tdp.qam.fan.mode"].to(str)
255
+ if mode != "disabled":
256
+ with self.fan_lock:
257
+ if conf[f"tdp.qam.fan.{mode}.reset"].to(bool):
258
+ conf[f"tdp.qam.fan.{mode}.reset"] = False
259
+ curve = DEFAULT_EDGE if "edge" in mode else DEFAULT_TCTL
260
+ for k, v in curve.items():
261
+ if f"tdp.qam.fan.{mode}.st{k}" in conf:
262
+ conf[f"tdp.qam.fan.{mode}.st{k}"] = v
263
+
264
+ for k, v in conf[f"tdp.qam.fan.{mode}"].to(dict).items():
265
+ if not k.startswith("st"):
266
+ continue
267
+ self.fan_curve[int(k[2:])] = v / 100
268
+ if self.fan_state:
269
+ s = self.fan_state
270
+ fan_speed = (
271
+ f"{s['v_curr']*100:.1f}% @ {s['t_target']}C"
272
+ if s["in_setpoint"]
273
+ else f"{s['v_curr']*100:.1f}% → {s['v_target']*100:.1f}%"
274
+ )
275
+ conf[f"tdp.qam.fan.{mode}.info"] = (
276
+ f"{fan_speed} ({', '.join(map(str, s['v_rpm']))} RPM)\n"
277
+ + f"Tctl: {s['t_junction']:.2f}C, "
278
+ + f"Edge: {s['t_edge']:.2f}C\n"
279
+ )
280
+ if "junction" in mode:
281
+ self.fan_junction.set()
282
+ else:
283
+ self.fan_junction.clear()
284
+
285
+ if not self.fan_t:
286
+ self.fan_should_exit.clear()
287
+ self.fan_t = Thread(
288
+ target=fan_worker,
289
+ args=(
290
+ self.fan_info,
291
+ self.fan_should_exit,
292
+ self.fan_lock,
293
+ self.fan_curve,
294
+ self.fan_state,
295
+ self.fan_junction,
296
+ ),
297
+ )
298
+ self.fan_t.start()
299
+ else:
300
+ if self.fan_t:
301
+ self.fan_should_exit.set()
302
+ self.fan_t.join()
303
+ self.fan_t = None
304
+ self.fan_state = {}
305
+
194
306
  def notify(self, events: Sequence[Event]):
195
307
  for ev in events:
196
308
  if ev["type"] == "tdp":
197
309
  self.sys_tdp = True
198
310
  self.new_tdp = ev["tdp"]
199
- self.sys_tdp = ev['tdp'] is not None
311
+ self.sys_tdp = ev["tdp"] is not None
200
312
 
201
313
  if ev["type"] == "ppd":
202
314
  # TODO: Make tunable per device
@@ -207,13 +319,19 @@ class SmuQamPlugin(HHDPlugin):
207
319
  self.new_tdp = 15
208
320
  case "performance":
209
321
  self.new_tdp = 25
210
-
211
- if ev['type'] == 'special' and ev.get('event', None) == "wakeup":
212
- logger.info(f"Waking up from sleep, resetting TDP after {SLEEP_DELAY} seconds.")
322
+
323
+ if ev["type"] == "special" and ev.get("event", None) == "wakeup":
324
+ logger.info(
325
+ f"Waking up from sleep, resetting TDP after {SLEEP_DELAY} seconds."
326
+ )
213
327
  self.queued = time.time() + SLEEP_DELAY
214
328
 
215
329
  def close(self):
216
- pass
330
+ if self.fan_t:
331
+ self.fan_should_exit.set()
332
+ self.fan_t.join()
333
+ self.fan_t = None
334
+ self.fan_state = {}
217
335
 
218
336
 
219
337
  class SmuDriverPlugin(HHDPlugin):
@@ -0,0 +1,80 @@
1
+ title: TDP Settings
2
+ type: container
3
+ tags: [ hide-title ]
4
+ children:
5
+ tdp:
6
+ title: TDP
7
+ hint: >-
8
+ Controls all Ryzen SMU settings through preset curves.
9
+ type: int
10
+ step: 1
11
+ unit: W
12
+
13
+ boost:
14
+ title: TDP Boost
15
+ type: bool
16
+ default: True
17
+
18
+ sys_tdp:
19
+ type: display
20
+ title: " "
21
+ tags: []
22
+
23
+ fan:
24
+ type: mode
25
+ title: Custom Fan Curve
26
+ hint: >-
27
+ Allows you to set a custom fan curve and to choose the temperature
28
+ probe (Edge or Junction). Junction is the peak temperature of the
29
+ chip: responds faster and prevents throttling. Edge is the temperature
30
+ of the chip: responds slower and prevents overheating.
31
+
32
+ default: disabled
33
+ modes:
34
+ disabled:
35
+ type: container
36
+ title: Disabled
37
+
38
+ manual_edge:
39
+ type: container
40
+ title: Manual (Edge, Smooth)
41
+ tags: [ non-essential ]
42
+ children:
43
+
44
+ info:
45
+ title: ""
46
+ type: display
47
+
48
+ st40: &speed_template
49
+ hint: Sets the speed at the named temperature.
50
+ tags: [slim]
51
+ type: int
52
+ min: 0
53
+ max: 100
54
+ step: 2
55
+ unit: "%"
56
+ title: 40C
57
+ default: 25
58
+
59
+ reset:
60
+ type: action
61
+ title: Reset to Default
62
+
63
+ manual_junction:
64
+ type: container
65
+ title: Manual (Tctl, Fast)
66
+ tags: [ non-essential ]
67
+ children:
68
+
69
+ info:
70
+ title: ""
71
+ type: display
72
+
73
+ st40:
74
+ <<: *speed_template
75
+ title: 40C
76
+ default: 40
77
+
78
+ reset:
79
+ type: action
80
+ title: Reset to Default
@@ -25,10 +25,7 @@ tdp:
25
25
  tags: [ non-essential ]
26
26
  default: >
27
27
  Handheld Daemon supports the TDP slider in Steam and can use its per
28
- game profiles now. For the Legion Go and Ally, it also supports custom
29
- fan curves and battery limits.
30
- Consider it, unless you prefer Decky.
31
- (Powercontrol supports fan curves for GPD and Ayaneo devices)
28
+ game profiles. It also supports custom fan curves and battery limits.
32
29
 
33
30
  decky_remove:
34
31
  title: Disable Decky TDP Plugins
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: adjustor
3
- Version: 3.5.4
3
+ Version: 3.6.1
4
4
  Summary: Adjustor, a userspace program for managing the TDP of handheld devices.
5
5
  Author-email: Kapenekakis Antheas <pypi@antheas.dev>
6
6
  Project-URL: Homepage, https://github.com/hhd-dev/adjustor
File without changes
@@ -1,21 +0,0 @@
1
- title: TDP Settings
2
- type: container
3
- tags: [ hide-title ]
4
- children:
5
- tdp:
6
- title: TDP
7
- hint: >-
8
- Controls all Ryzen SMU settings through preset curves.
9
- type: int
10
- step: 1
11
- unit: W
12
-
13
- boost:
14
- title: TDP Boost
15
- type: bool
16
- default: True
17
-
18
- sys_tdp:
19
- type: display
20
- title: " "
21
- tags: []
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes