adjustor 0.1.1__tar.gz → 0.2.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. {adjustor-0.1.1/src/adjustor.egg-info → adjustor-0.2.0}/PKG-INFO +20 -7
  2. {adjustor-0.1.1 → adjustor-0.2.0}/pyproject.toml +1 -1
  3. {adjustor-0.1.1 → adjustor-0.2.0}/readme.md +19 -6
  4. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/core/const.py +13 -1
  5. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/lenovo/settings.yml +0 -2
  6. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/smu/__init__.py +58 -30
  7. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/smu/qam.yml +0 -11
  8. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/smu/smu.yml +15 -2
  9. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/hhd.py +31 -15
  10. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/settings.yml +1 -0
  11. {adjustor-0.1.1 → adjustor-0.2.0/src/adjustor.egg-info}/PKG-INFO +20 -7
  12. {adjustor-0.1.1 → adjustor-0.2.0}/LICENSE +0 -0
  13. {adjustor-0.1.1 → adjustor-0.2.0}/MANIFEST.in +0 -0
  14. {adjustor-0.1.1 → adjustor-0.2.0}/setup.cfg +0 -0
  15. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/__init__.py +0 -0
  16. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/__main__.py +0 -0
  17. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/core/acpi.py +0 -0
  18. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/core/alib.py +0 -0
  19. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/core/asus.py +0 -0
  20. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/core/lenovo.py +0 -0
  21. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/__init__.py +0 -0
  22. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor/drivers/lenovo/__init__.py +0 -0
  23. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor.egg-info/SOURCES.txt +0 -0
  24. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor.egg-info/dependency_links.txt +0 -0
  25. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor.egg-info/entry_points.txt +0 -0
  26. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor.egg-info/requires.txt +0 -0
  27. {adjustor-0.1.1 → adjustor-0.2.0}/src/adjustor.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: adjustor
3
- Version: 0.1.1
3
+ Version: 0.2.0
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
@@ -35,22 +35,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
35
35
  for the Legion Go.
36
36
 
37
37
  ## AMD TDP Control
38
- Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface,
39
- which exposes a superset of the parameters that can be found in
38
+ Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
39
+ of AMD, which exposes a superset of the parameters that can be currently found in
40
40
  [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
41
- This vendor function is part of the ACPI ASL library, and provided through the
41
+ This vendor interface is part of the ACPI ASL library, and provided through the
42
42
  ALIB method 0x0C.
43
+ The underlying implementation of the interface is SMU calls.
43
44
  This means that as long as the kernel module `acpi_call` is loaded, Adjustor
44
45
  can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
45
46
 
46
- ALIB does not provide a way for reading the performance metrics table, so
47
- Adjustor can only write TDP values.
47
+ Right now, Adjustor only implements a subset of useful ALIB parameters that are
48
+ well documented.
49
+ In addition, ALIB does not provide a way for reading the performance metrics table,
50
+ so Adjustor can only write (not read) TDP values.
48
51
  From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
49
52
  interacts with ALIB, it was found that there are at least 10 parameters which control
50
53
  the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
51
54
 
52
55
  ## Installation
53
- Installation instructions coming the following days.
56
+ Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
57
+ and provided Handheld Daemon has been installed through
58
+ [AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
59
+ on restart.
60
+ COPR coming soon.
61
+
62
+ Alternatively, on a local install of Handheld Daemon you may:
63
+ ```bash
64
+ ~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
65
+ ```
66
+ However, the autoupdater in Handheld Daemon does not support updating yet.
54
67
 
55
68
  ## Development
56
69
  Install to the same virtual environment as hhd to have Adjustor picked up
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adjustor"
3
- version = "0.1.1"
3
+ version = "0.2.0"
4
4
  authors = [
5
5
  { name="Kapenekakis Antheas", email="pypi@antheas.dev" },
6
6
  ]
@@ -18,22 +18,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
18
18
  for the Legion Go.
19
19
 
20
20
  ## AMD TDP Control
21
- Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface,
22
- which exposes a superset of the parameters that can be found in
21
+ Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
22
+ of AMD, which exposes a superset of the parameters that can be currently found in
23
23
  [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
24
- This vendor function is part of the ACPI ASL library, and provided through the
24
+ This vendor interface is part of the ACPI ASL library, and provided through the
25
25
  ALIB method 0x0C.
26
+ The underlying implementation of the interface is SMU calls.
26
27
  This means that as long as the kernel module `acpi_call` is loaded, Adjustor
27
28
  can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
28
29
 
29
- ALIB does not provide a way for reading the performance metrics table, so
30
- Adjustor can only write TDP values.
30
+ Right now, Adjustor only implements a subset of useful ALIB parameters that are
31
+ well documented.
32
+ In addition, ALIB does not provide a way for reading the performance metrics table,
33
+ so Adjustor can only write (not read) TDP values.
31
34
  From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
32
35
  interacts with ALIB, it was found that there are at least 10 parameters which control
33
36
  the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
34
37
 
35
38
  ## Installation
36
- Installation instructions coming the following days.
39
+ Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
40
+ and provided Handheld Daemon has been installed through
41
+ [AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
42
+ on restart.
43
+ COPR coming soon.
44
+
45
+ Alternatively, on a local install of Handheld Daemon you may:
46
+ ```bash
47
+ ~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
48
+ ```
49
+ However, the autoupdater in Handheld Daemon does not support updating yet.
37
50
 
38
51
  ## Development
39
52
  Install to the same virtual environment as hhd to have Adjustor picked up
@@ -1,5 +1,12 @@
1
1
  from .alib import A, D, DeviceParams, AlibParams
2
2
 
3
+ ROG_ALLY_PP_MAP = [
4
+ ("low-power", 0),
5
+ ("quiet", 0),
6
+ ("balanced", 13),
7
+ ("performance", 20),
8
+ ]
9
+
3
10
  ALIB_PARAMS = {
4
11
  # TDPs
5
12
  "stapm_limit": A(0x05, 0, 54, 1000),
@@ -32,9 +39,14 @@ DEV_PARAMS_6040: dict[str, DeviceParams] = DEV_PARAMS_7040
32
39
 
33
40
  DEV_PARAMS_LEGO = DEV_PARAMS_7040
34
41
 
42
+ DEV_DATA: dict[str, tuple[dict[str, DeviceParams], dict[str, AlibParams], bool]] = {
43
+ "NEO-01": (DEV_PARAMS_7040, ALIB_PARAMS_7040, False),
44
+ "83E1": (DEV_PARAMS_LEGO, ALIB_PARAMS_7040, False),
45
+ }
46
+
35
47
  CPU_DATA: dict[str, tuple[dict[str, DeviceParams], dict[str, AlibParams]]] = {
36
48
  "AMD Ryzen Z1 Extreme": (DEV_PARAMS_7040, ALIB_PARAMS_7040),
37
- "AMD Ryzen 7 7800U": (DEV_PARAMS_7040, ALIB_PARAMS_7040),
49
+ "AMD Ryzen 7 7840U": (DEV_PARAMS_7040, ALIB_PARAMS_7040),
38
50
  # GPD Win 4
39
51
  # model name : AMD Ryzen 7 6800U with Radeon Graphics
40
52
  # 28W works fine, 30W is pushing it
@@ -57,8 +57,6 @@ children:
57
57
  modes at your own risk.
58
58
 
59
59
  This fan curve needs to be reapplied every time you switch TDP modes.
60
- Adjustor will automatically apply it for changes made through the UI
61
- but not with Legion L + Y.
62
60
  default: disabled
63
61
  modes:
64
62
  disabled:
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import time
2
3
 
3
4
  from hhd.plugins import Context, HHDPlugin, load_relative_yaml
4
5
  from hhd.plugins.conf import Config
@@ -38,10 +39,15 @@ def get_platform_profile():
38
39
  return None
39
40
 
40
41
 
42
+ PP_DELAY = 0.2
43
+
44
+
41
45
  class SmuQamPlugin(HHDPlugin):
42
46
 
43
47
  def __init__(
44
- self, dev: dict[str, DeviceParams], platform_profile: bool = True
48
+ self,
49
+ dev: dict[str, DeviceParams],
50
+ pp_map: list[tuple[str, int]] | None,
45
51
  ) -> None:
46
52
  self.name = f"adjustor_smu_qam"
47
53
  self.priority = 7
@@ -53,14 +59,16 @@ class SmuQamPlugin(HHDPlugin):
53
59
  self.emit = None
54
60
  self.old_conf = None
55
61
 
56
- self.check_pp = platform_profile
57
- self.old_pp = None
58
- self.has_pp = False
59
-
60
62
  self.old_tdp = None
61
63
  self.old_boost = None
62
64
  self.is_set = False
63
65
 
66
+ self.pp_map = pp_map
67
+ if pp_map:
68
+ self.pps = get_platform_choices() or []
69
+ else:
70
+ self.pps = []
71
+
64
72
  def settings(self):
65
73
  if not self.enabled:
66
74
  self.initialized = False
@@ -69,18 +77,6 @@ class SmuQamPlugin(HHDPlugin):
69
77
  self.initialized = True
70
78
  out = {"tdp": {"qam": load_relative_yaml("qam.yml")}}
71
79
 
72
- # Limit platform profile choices or remove
73
- choices = get_platform_choices()
74
- if choices and self.check_pp:
75
- options = out["tdp"]["qam"]["children"]["platform_profile"]["options"]
76
- for c in list(options):
77
- if c not in choices:
78
- del options[c]
79
- self.has_pp = True
80
- else:
81
- del out["tdp"]["qam"]["children"]["platform_profile"]
82
- self.has_pp = False
83
-
84
80
  # Set device limits based on stapm
85
81
  lims = self.dev.get("skin_limit", self.dev.get("stapm_limit", None))
86
82
  assert (
@@ -112,17 +108,6 @@ class SmuQamPlugin(HHDPlugin):
112
108
  if not self.enabled or not self.initialized:
113
109
  return
114
110
 
115
- if self.has_pp:
116
- cpp = conf["tdp.qam.platform_profile"].to(str)
117
-
118
- if cpp and cpp != self.old_pp:
119
- logger.info(f"Setting platform profile to '{cpp}'")
120
- set_platform_profile(cpp)
121
-
122
- pp = get_platform_profile()
123
- conf["tdp.qam.platform_profile"] = pp
124
- self.old_pp = pp
125
-
126
111
  new_tdp = conf["tdp.qam.tdp"].to(int)
127
112
  new_boost = conf["tdp.qam.boost"].to(bool)
128
113
  changed = (
@@ -135,6 +120,14 @@ class SmuQamPlugin(HHDPlugin):
135
120
 
136
121
  conf["tdp.smu.std.skin_limit"] = new_tdp
137
122
  conf["tdp.smu.std.stapm_limit"] = new_tdp
123
+
124
+ if self.pp_map and conf["tdp.smu.platform_profile"].to(str) != "disabled":
125
+ pp = self.pp_map[0][0]
126
+ for npp, tdp in self.pp_map:
127
+ if tdp < new_tdp and npp in self.pps:
128
+ pp = npp
129
+ conf["tdp.smu.platform_profile"] = pp
130
+
138
131
  if new_boost:
139
132
  try:
140
133
  fmax = self.dev["fast_limit"].smax
@@ -174,6 +167,7 @@ class SmuDriverPlugin(HHDPlugin):
174
167
  self,
175
168
  dev: dict[str, DeviceParams],
176
169
  cpu: dict[str, AlibParams],
170
+ platform_profile: bool = True,
177
171
  ) -> None:
178
172
  self.name = f"adjustor_smu"
179
173
  self.priority = 9
@@ -185,7 +179,11 @@ class SmuDriverPlugin(HHDPlugin):
185
179
  self.dev = dev
186
180
  self.cpu = cpu
187
181
 
182
+ self.check_pp = platform_profile
183
+ self.has_pp = False
184
+ self.old_pp = None
188
185
  self.old_vals = {}
186
+ self.is_set = False
189
187
 
190
188
  for k in dev:
191
189
  assert (
@@ -203,6 +201,18 @@ class SmuDriverPlugin(HHDPlugin):
203
201
  }
204
202
  }
205
203
 
204
+ # Limit platform profile choices or remove
205
+ choices = get_platform_choices()
206
+ if choices and self.check_pp:
207
+ options = out["tdp"]["smu"]["children"]["platform_profile"]["options"]
208
+ for c in list(options):
209
+ if c not in choices and c != "disabled":
210
+ del options[c]
211
+ self.has_pp = True
212
+ else:
213
+ del out["tdp"]["smu"]["children"]["platform_profile"]
214
+ self.has_pp = False
215
+
206
216
  # Remove unsupported instructions
207
217
  # Add absolute limits based on CPU
208
218
  std = out["tdp"]["smu"]["children"]["std"]["children"]
@@ -270,19 +280,37 @@ class SmuDriverPlugin(HHDPlugin):
270
280
  new_vals[k] = v
271
281
 
272
282
  if set(new_vals.items()) != set(self.old_vals.items()):
273
- conf["tdp.smu.status"] = "Not Set"
283
+ self.is_set = False
284
+
285
+ if self.has_pp:
286
+ new_pp = conf["tdp.smu.platform_profile"].to(str)
287
+ if new_pp != self.old_pp and new_pp != "disabled":
288
+ self.is_set = False
289
+ self.old_pp = new_pp
274
290
 
275
291
  if conf["tdp.smu.apply"].to(bool):
276
292
  conf["tdp.smu.apply"] = False
293
+
294
+ if self.has_pp:
295
+ cpp = conf["tdp.smu.platform_profile"].to(str)
296
+ if cpp != "disabled":
297
+ logger.info(f"Setting platform profile to '{cpp}'")
298
+ set_platform_profile(cpp)
299
+ time.sleep(PP_DELAY)
300
+
277
301
  alib(
278
302
  new_vals,
279
303
  self.cpu,
280
304
  limit="device" if self.enforce_limits else "cpu",
281
305
  dev=self.dev,
282
306
  )
283
- conf["tdp.smu.status"] = "Set"
307
+ self.is_set = True
284
308
 
285
309
  self.old_vals = new_vals
310
+ if self.is_set:
311
+ conf["tdp.smu.status"] = "Set"
312
+ else:
313
+ conf["tdp.smu.status"] = "Not Set"
286
314
 
287
315
  def close(self):
288
316
  pass
@@ -1,17 +1,6 @@
1
1
  title: Quick Settings
2
2
  type: container
3
3
  children:
4
- platform_profile:
5
- type: multiple
6
- title: Platform Profile
7
- options:
8
- low-power: Low Power
9
- cool: Cool
10
- quiet: Quiet
11
- balanced: Balanced
12
- balanced-performance: Balanced Performance
13
- performance: Performance
14
-
15
4
  tdp:
16
5
  title: TDP
17
6
  hint: >-
@@ -1,11 +1,11 @@
1
1
  type: container
2
- title: Ryzen SMU Configurator
2
+ title: Advanced Configurator
3
3
  tags: [advanced, expert]
4
4
  hint: >-
5
5
  children:
6
6
  apply:
7
7
  type: action
8
- title: Apply SMU Settings
8
+ title: Apply Settings
9
9
 
10
10
  status:
11
11
  title: TDP Status
@@ -14,6 +14,19 @@ children:
14
14
  choices:
15
15
  uninit: Not Set
16
16
  init: Set
17
+
18
+ platform_profile:
19
+ type: multiple
20
+ title: Platform Profile
21
+ default: balanced
22
+ options:
23
+ disabled: Not Set
24
+ low-power: Low Power
25
+ cool: Cool
26
+ quiet: Quiet
27
+ balanced: Balanced
28
+ balanced-performance: Balanced Performance
29
+ performance: Performance
17
30
 
18
31
  std:
19
32
  tags: [advanced]
@@ -1,7 +1,7 @@
1
1
  from adjustor.core.acpi import initialize, check_perms
2
2
 
3
3
  from typing import Sequence
4
- from adjustor.core.const import CPU_DATA
4
+ from adjustor.core.const import CPU_DATA, ROG_ALLY_PP_MAP, DEV_DATA
5
5
 
6
6
  import os
7
7
  from hhd.plugins import (
@@ -56,7 +56,10 @@ class AdjustorPlugin(HHDPlugin):
56
56
  self.enfoce_limits = True
57
57
 
58
58
  def settings(self) -> HHDSettings:
59
- return {"tdp": {"general": load_relative_yaml("settings.yml")}}
59
+ out = {"tdp": {"general": load_relative_yaml("settings.yml")}}
60
+ if os.environ.get("HHD_ADJ_ENABLE_TDP"):
61
+ out['tdp']['general']['children']['enable']['default'] = True
62
+ return out
60
63
 
61
64
  def open(
62
65
  self,
@@ -91,27 +94,40 @@ def autodetect(existing: Sequence[HHDPlugin]) -> Sequence[HHDPlugin]:
91
94
  cpuinfo = f.read().strip()
92
95
 
93
96
  drivers_matched = False
94
- legion_go = False
95
97
  if prod == "83E1":
96
98
  drivers.append(LenovoDriverPlugin())
97
99
  drivers_matched = True
98
- legion_go = True
99
100
 
100
- if (
101
- os.environ.get("HHD_ADJ_DEBUG")
102
- or os.environ.get("HHD_ENABLE_SMU")
103
- or not drivers_matched
104
- ):
101
+ if os.environ.get("HHD_ADJ_DEBUG") or os.environ.get("HHD_ENABLE_SMU"):
102
+ drivers_matched = False
103
+
104
+ if not drivers_matched and prod in DEV_DATA:
105
+ dev, cpu, pp_enable = DEV_DATA[prod]
106
+ pp_enable |= bool(os.environ.get("HHD_ADJ_DEBUG"))
107
+ drivers.append(
108
+ SmuDriverPlugin(
109
+ dev,
110
+ cpu,
111
+ platform_profile=pp_enable,
112
+ )
113
+ )
114
+ drivers.append(
115
+ SmuQamPlugin(dev, ROG_ALLY_PP_MAP if pp_enable else None),
116
+ )
117
+ drivers_matched = True
118
+
119
+ if not drivers_matched:
105
120
  for name, (dev, cpu) in CPU_DATA.items():
106
121
  if name in cpuinfo:
107
- drivers.append(SmuDriverPlugin(dev, cpu))
108
122
  drivers.append(
109
- SmuQamPlugin(
123
+ SmuDriverPlugin(
110
124
  dev,
111
- platform_profile=(
112
- not legion_go or bool(os.environ.get("HHD_ADJ_DEBUG"))
113
- ),
114
- ),
125
+ cpu,
126
+ platform_profile=True,
127
+ )
128
+ )
129
+ drivers.append(
130
+ SmuQamPlugin(dev, ROG_ALLY_PP_MAP),
115
131
  )
116
132
  break
117
133
 
@@ -10,6 +10,7 @@ children:
10
10
  enforce_limits:
11
11
  title: Enforce Device Limits
12
12
  type: bool
13
+ tags: [ expert ]
13
14
  default: True
14
15
 
15
16
  error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: adjustor
3
- Version: 0.1.1
3
+ Version: 0.2.0
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
@@ -35,22 +35,35 @@ As part of the latest Lenovo bios, it also allows for setting a custom fan curve
35
35
  for the Legion Go.
36
36
 
37
37
  ## AMD TDP Control
38
- Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface,
39
- which exposes a superset of the parameters that can be found in
38
+ Adjustor controls TDP through the Dynamic Power and Thermal Configuration Interface
39
+ of AMD, which exposes a superset of the parameters that can be currently found in
40
40
  [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/), through ACPI.
41
- This vendor function is part of the ACPI ASL library, and provided through the
41
+ This vendor interface is part of the ACPI ASL library, and provided through the
42
42
  ALIB method 0x0C.
43
+ The underlying implementation of the interface is SMU calls.
43
44
  This means that as long as the kernel module `acpi_call` is loaded, Adjustor
44
45
  can control TDP in an equivalent way to [RyzenAdj](https://github.dev/FlyGoat/RyzenAdj/).
45
46
 
46
- ALIB does not provide a way for reading the performance metrics table, so
47
- Adjustor can only write TDP values.
47
+ Right now, Adjustor only implements a subset of useful ALIB parameters that are
48
+ well documented.
49
+ In addition, ALIB does not provide a way for reading the performance metrics table,
50
+ so Adjustor can only write (not read) TDP values.
48
51
  From reverse engineering the Legion Go (see [here](./alib.md)), and seeing how it
49
52
  interacts with ALIB, it was found that there are at least 10 parameters which control
50
53
  the method STTv2 and are not part of RyzenAdj or have been documented elsewhere.
51
54
 
52
55
  ## Installation
53
- Installation instructions coming the following days.
56
+ Adjustor is available on [AUR](https://aur.archlinux.org/packages/adjustor)
57
+ and provided Handheld Daemon has been installed through
58
+ [AUR](https://aur.archlinux.org/packages/hhd) too, it will load it automatically
59
+ on restart.
60
+ COPR coming soon.
61
+
62
+ Alternatively, on a local install of Handheld Daemon you may:
63
+ ```bash
64
+ ~/.local/share/hhd/venv/bin/pip install --upgrade adjustor
65
+ ```
66
+ However, the autoupdater in Handheld Daemon does not support updating yet.
54
67
 
55
68
  ## Development
56
69
  Install to the same virtual environment as hhd to have Adjustor picked up
File without changes
File without changes
File without changes