Skip to content

Commit d82bb45

Browse files
committed
Address Copilot review: SMC cleanup, CF leak, fan_name, IOReport power metrics
- Add atexit hook to close SMC connection on exit - CFRelease per-group channels after IOReportMergeChannels - Honor fan_name parameter in Cpu.fan_percent() - Fix except Exception → except BaseException for sys.exit fallback - Wire IOReport power data (CPU/GPU/Total watts) as custom sensors - Add power metrics to PurpleTheme-AppleSilicon
1 parent 62d2e99 commit d82bb45

4 files changed

Lines changed: 99 additions & 4 deletions

File tree

library/sensors/sensors_apple_silicon.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ def _open(self):
114114
return
115115
self._conn = conn.value
116116
self._available = True
117+
import atexit
118+
atexit.register(self.close)
119+
120+
def close(self):
121+
if self._conn is not None and self._available:
122+
self._iokit.IOServiceClose(self._conn)
123+
self._conn = None
124+
self._available = False
117125

118126
def _fcc(self, s):
119127
return int.from_bytes(s.encode('ascii'), byteorder='big')
@@ -312,6 +320,7 @@ def _subscribe(self):
312320
merged = ch
313321
else:
314322
self._iorep.IOReportMergeChannels(merged, ch, None)
323+
self._cf.CFRelease(ch)
315324

316325
if not merged:
317326
logger.warning("IOReport: no channels found")
@@ -588,11 +597,17 @@ def temperature() -> float:
588597
@staticmethod
589598
def fan_percent(fan_name: str = None) -> float:
590599
smc = _get_smc()
591-
actual = smc.read_float('F0Ac')
600+
fan_idx = 0
601+
if fan_name and fan_name != "AUTO":
602+
try:
603+
fan_idx = int(fan_name.replace('F', '').replace('Ac', ''))
604+
except (ValueError, AttributeError):
605+
logger.debug(f"Apple Silicon: ignoring manual CPU_FAN '{fan_name}', using fan 0")
606+
actual = smc.read_float(f'F{fan_idx}Ac')
592607
if math.isnan(actual):
593608
return math.nan
594-
mn = smc.read_float('F0Mn')
595-
mx = smc.read_float('F0Mx')
609+
mn = smc.read_float(f'F{fan_idx}Mn')
610+
mx = smc.read_float(f'F{fan_idx}Mx')
596611
if math.isnan(mn) or math.isnan(mx) or mx <= mn:
597612
return math.nan
598613
if actual <= 0:

library/sensors/sensors_custom.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,51 @@ def as_string(self) -> str:
9999
def last_values(self) -> List[float]:
100100
# If a custom data class only has text values, it won't be possible to display line graph
101101
pass
102+
103+
104+
# Apple Silicon IOReport power metrics (only functional when APPLE_SILICON sensors are active)
105+
class _IOReportPowerBase(CustomDataSource):
106+
_key = ''
107+
_history = None
108+
109+
def _get_power(self) -> float:
110+
try:
111+
from library.sensors.sensors_apple_silicon import _get_ioreport
112+
data = _get_ioreport().get_data()
113+
return data.get(self._key, math.nan)
114+
except Exception:
115+
return math.nan
116+
117+
def as_numeric(self) -> float:
118+
val = self._get_power()
119+
if self._history is None:
120+
self._history = [math.nan] * 30
121+
self._history.append(val if not math.isnan(val) else 0)
122+
self._history.pop(0)
123+
return val
124+
125+
def as_string(self) -> str:
126+
val = self.as_numeric()
127+
if math.isnan(val):
128+
return "--W"
129+
return f'{val:.1f}W'
130+
131+
def last_values(self) -> List[float]:
132+
if self._history is None:
133+
return [math.nan] * 30
134+
return list(self._history)
135+
136+
137+
class AppleSiliconCPUPower(_IOReportPowerBase):
138+
_key = 'cpu_power'
139+
_history = [math.nan] * 30
140+
141+
142+
class AppleSiliconGPUPower(_IOReportPowerBase):
143+
_key = 'gpu_power'
144+
_history = [math.nan] * 30
145+
146+
147+
class AppleSiliconTotalPower(_IOReportPowerBase):
148+
_key = 'total_power'
149+
_history = [math.nan] * 30

library/stats.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
logger.error("Apple Silicon sensor support is only available on Apple Silicon Macs")
7676
try:
7777
sys.exit(1)
78-
except Exception:
78+
except BaseException:
7979
os._exit(1)
8080
elif HW_SENSORS == "AUTO":
8181
if platform.system() == 'Windows':

res/themes/PurpleTheme-AppleSilicon/theme.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,38 @@ STATS:
182182
FONT_SIZE: 20
183183
FONT_COLOR: 200, 255, 200
184184
BACKGROUND_IMAGE: background.png
185+
CUSTOM:
186+
INTERVAL: 2
187+
AppleSiliconCPUPower:
188+
TEXT:
189+
SHOW: True
190+
X: 405
191+
Y: 240
192+
FONT: jetbrains-mono/JetBrainsMono-Bold.ttf
193+
FONT_SIZE: 18
194+
FONT_COLOR: 249, 200, 100
195+
BACKGROUND_IMAGE: background.png
196+
WIDTH: 80
197+
AppleSiliconGPUPower:
198+
TEXT:
199+
SHOW: True
200+
X: 535
201+
Y: 240
202+
FONT: jetbrains-mono/JetBrainsMono-Bold.ttf
203+
FONT_SIZE: 18
204+
FONT_COLOR: 203, 118, 255
205+
BACKGROUND_IMAGE: background.png
206+
WIDTH: 80
207+
AppleSiliconTotalPower:
208+
TEXT:
209+
SHOW: True
210+
X: 665
211+
Y: 240
212+
FONT: jetbrains-mono/JetBrainsMono-Bold.ttf
213+
FONT_SIZE: 18
214+
FONT_COLOR: 255, 180, 100
215+
BACKGROUND_IMAGE: background.png
216+
WIDTH: 80
185217
NET:
186218
INTERVAL: 1
187219
WLO:

0 commit comments

Comments
 (0)