Coverage for linuxpy/thermal.py: 71%

58 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-13 08:09 +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 

23 

24from linuxpy.device import device_number 

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

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

27from linuxpy.util import make_find 

28 

29 

30class ThermalZone(Device): 

31 """ 

32 Thermal sensor 

33 

34 Attributes: 

35 type (str): thermal zone type 

36 policy (str): the policy 

37 available_policies list[str]: list of available policies 

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

39 offset (int): offet in milli-celsius 

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

41 device_number (int): thermal device number 

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

43 """ 

44 

45 type = Str("type") 

46 policy = Str("policy") 

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

48 temperature = Int("temp") 

49 offset = Int("offset") 

50 mode = Attr("mode", Mode) 

51 

52 @classmethod 

53 def from_id(cls, n): 

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

55 

56 @property 

57 def trip_points(self): 

58 result = [] 

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

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

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

62 return result 

63 

64 @property 

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

66 return device_number(self.syspath) 

67 

68 

69class TripPoint: 

70 """ 

71 Trip point associated with the thermal zone 

72 

73 Attributes: 

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

75 type (str): trip point type 

76 """ 

77 

78 def __init__(self, temperature_path, type_path): 

79 self.temperature_path = temperature_path 

80 self.type_path = type_path 

81 

82 @property 

83 def temperature(self) -> int: 

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

85 return int(f.read()) 

86 

87 @property 

88 def type(self) -> str: 

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

90 return f.read().strip() 

91 

92 

93class CoolingDevice(Device): 

94 """ 

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

96 

97 Attributes: 

98 type (str): thermal zone type 

99 """ 

100 

101 type = Str("type") 

102 state = Int("cur_state") 

103 max_state = Int("max_state") 

104 

105 @property 

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

107 return device_number(self.syspath) 

108 

109 

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

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

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

113 

114 

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

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

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

118 

119 

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

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

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

123 

124 

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

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

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

128 

129 

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

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

132 yield from iter_thermal_zone_devices() 

133 yield from iter_cooling_devices() 

134 

135 

136_find = make_find(iter_devices) 

137 

138 

139def find( 

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

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

142 """ 

143 If find_all is False: 

144 

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

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

147 Default is to return a random first device. 

148 

149 If find_all is True: 

150 

151 The result is an iterator. 

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

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

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

155 """ 

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