Coverage for linuxpy/thermal.py: 0%

69 statements  

« prev     ^ index     » next       coverage.py v7.6.8, created at 2025-05-27 13:54 +0200

1# 

2# This file is part of the linuxpy project 

3# 

4# Copyright (c) 2023 Tiago Coutinho 

5# Distributed under the GPLv3 license. See LICENSE for more info. 

6 

7""" 

8Human friendly interface to linux thermal subsystem. 

9 

10The heart of linuxpy thermal library are the [`ThermalZone`][linuxpy.thermal.ThermalZone] 

11and [`CoolingDevice`][linuxpy.thermal.CoolingDevice] classes. 

12 

13Probably the most common way to create a thermal device is through 

14the [`find`][linuxpy.thermal.find] helper: 

15 

16```python 

17with find(type="x86_pkg_temp") as tz: 

18 print(f"X86 temperature: {tz.temperature/1000:6.2f} C") 

19``` 

20""" 

21 

22import pathlib 

23import time 

24 

25from linuxpy.device import device_number 

26from linuxpy.sysfs import THERMAL_PATH, Attr, Device, Int, Mode, Str 

27from linuxpy.types import Callable, Iterable, Optional, Union 

28from linuxpy.util import make_find 

29 

30 

31class ThermalZone(Device): 

32 """ 

33 Thermal sensor 

34 

35 Attributes: 

36 type (str): thermal zone type 

37 policy (str): the policy 

38 available_policies list[str]: list of available policies 

39 temperature (int): current temperature in milli-celsius 

40 offset (int): offet in milli-celsius 

41 mode (Mode): current mode (enabled/disabled) 

42 device_number (int): thermal device number 

43 trip_points (list[TripPoint]): list of trip points (new list every time) 

44 """ 

45 

46 type = Str("type") 

47 policy = Str("policy") 

48 available_policies = Attr("available_policies", str.split) 

49 temperature = Int("temp") 

50 offset = Int("offset") 

51 mode = Attr("mode", Mode) 

52 

53 @classmethod 

54 def from_id(cls, n): 

55 return cls.from_syspath(THERMAL_PATH / f"thermal_zone{n}") 

56 

57 @property 

58 def trip_points(self): 

59 result = [] 

60 for temp_path in self.syspath.glob("trip_point_*_temp"): 

61 type_path = temp_path.with_name(temp_path.name.replace("_temp", "_type")) 

62 result.append(TripPoint(temp_path, type_path)) 

63 return result 

64 

65 @property 

66 def device_number(self) -> Optional[int]: 

67 return device_number(self.syspath) 

68 

69 

70class TripPoint: 

71 """ 

72 Trip point associated with the thermal zone 

73 

74 Attributes: 

75 temperature (int): trip point temperature in milli-celsius 

76 type (str): trip point type 

77 """ 

78 

79 def __init__(self, temperature_path, type_path): 

80 self.temperature_path = temperature_path 

81 self.type_path = type_path 

82 

83 @property 

84 def temperature(self) -> int: 

85 with self.temperature_path.open() as f: 

86 return int(f.read()) 

87 

88 @property 

89 def type(self) -> str: 

90 with self.type_path.open() as f: 

91 return f.read().strip() 

92 

93 

94class CoolingDevice(Device): 

95 """ 

96 Cooling device (fan, processor, ...) 

97 

98 Attributes: 

99 type (str): thermal zone type 

100 """ 

101 

102 type = Str("type") 

103 state = Int("cur_state") 

104 max_state = Int("max_state") 

105 

106 @property 

107 def device_number(self) -> Optional[int]: 

108 return device_number(self.syspath) 

109 

110 

111def iter_thermal_zone_paths() -> Iterable[pathlib.Path]: 

112 """Returns an iterator over all thermal zone paths""" 

113 yield from THERMAL_PATH.glob("thermal_zone*") 

114 

115 

116def iter_thermal_zone_devices() -> Iterable[ThermalZone]: 

117 """Returns an iterator over all thermal zone devices""" 

118 return (ThermalZone.from_syspath(path) for path in iter_thermal_zone_paths()) 

119 

120 

121def iter_cooling_device_paths() -> Iterable[pathlib.Path]: 

122 """Returns an iterator over all cooling device paths""" 

123 yield from THERMAL_PATH.glob("cooling_device*") 

124 

125 

126def iter_cooling_devices() -> Iterable[CoolingDevice]: 

127 """Returns an iterator over all cooling devices""" 

128 return (CoolingDevice.from_syspath(path) for path in iter_cooling_device_paths()) 

129 

130 

131def iter_devices() -> Iterable[Union[ThermalZone, CoolingDevice]]: 

132 """Returns an iterator over all thermal and cooling devices""" 

133 yield from iter_thermal_zone_devices() 

134 yield from iter_cooling_devices() 

135 

136 

137_find = make_find(iter_devices) 

138 

139 

140def find( 

141 find_all: bool = False, custom_match: Optional[Callable] = None, **kwargs 

142) -> Union[Device, Iterable[Device], None]: 

143 """ 

144 If find_all is False: 

145 

146 Find a device follwing the criteria matched by custom_match and kwargs. 

147 If no device is found matching the criteria it returns None. 

148 Default is to return a random first device. 

149 

150 If find_all is True: 

151 

152 The result is an iterator. 

153 Find all devices that match the criteria custom_match and kwargs. 

154 If no device is found matching the criteria it returns an empty iterator. 

155 Default is to return an iterator over all input devices found on the system. 

156 """ 

157 return _find(find_all, custom_match, **kwargs) 

158 

159 

160def main(): 

161 def acq(): 

162 for dev in sorted(iter_thermal_zone_devices(), key=lambda dev: dev.type): 

163 yield dev, dev.temperature 

164 

165 while True: 

166 read = acq() 

167 print(" | ".join(f"{dev.type} = {temp / 1000:6.2f}" for dev, temp in read), end="\r") 

168 time.sleep(0.1) 

169 

170 

171if __name__ == "__main__": 

172 main()