├── tests ├── config.nims └── test1.nim ├── examples ├── config.nims └── dump.nim ├── sensors.nimble ├── README.md └── src ├── sensors.nim └── sensors └── bindings.nim /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | -------------------------------------------------------------------------------- /examples/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") 2 | -------------------------------------------------------------------------------- /sensors.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.2.4" 4 | author = "inv2004" 5 | description = "libsensors wrapper" 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | 10 | # Dependencies 11 | 12 | requires "nim >= 1.6.8" 13 | -------------------------------------------------------------------------------- /examples/dump.nim: -------------------------------------------------------------------------------- 1 | import sensors 2 | import strformat 3 | 4 | init() 5 | 6 | for chip in chips(): 7 | echo fmt"{chip.name}: {chip.prefix}" 8 | for feature in chip: 9 | echo fmt" {feature.label:<35} ({feature.kind})" 10 | for subfeature in feature: 11 | echo fmt" {subfeature.name}: {subfeature.value:<20} ({subfeature.kind})" 12 | 13 | -------------------------------------------------------------------------------- /tests/test1.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import sensors 4 | 5 | init() 6 | 7 | test "chips": 8 | for chip in chips(): 9 | echo chip.name, ": ", chip.prefix 10 | 11 | test "prefix": 12 | echo chipWithPrefix("coretemp").name 13 | expect KeyError: 14 | echo chipWithPrefix("ccoretemp").name 15 | 16 | test "feature": 17 | for chip in chips(): 18 | for feature in chip.features(): 19 | echo feature.label 20 | 21 | test "kind": 22 | echo chipWithPrefix("coretemp").feature(FeatureTemp).name 23 | 24 | test "subfeature": 25 | echo chipWithPrefix("coretemp").feature(FeatureTemp).subfeature( 26 | SubfeatureTempInput).name 27 | 28 | test "value": 29 | echo chipWithPrefix("coretemp").feature(FeatureTemp).subfeature( 30 | SubfeatureTempInput).value 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sensors 2 | [libsensors](https://github.com/lm-sensors/lm-sensors) wrapper 3 | 4 | - [x] dynamic load and check of libsensors.so 5 | 6 | #### Example 7 | https://github.com/inv2004/sensors/blob/main/examples/dump.nim 8 | 9 | ```nim 10 | import sensors 11 | import strformat 12 | 13 | init() 14 | 15 | for chip in chips(): 16 | echo fmt"{chip.name}: {chip.prefix}" 17 | for feature in chip: 18 | echo fmt" {feature.label} ({feature.kind})" 19 | for subfeature in feature: 20 | echo fmt" {subfeature.name}: {subfeature.value} ({subfeature.kind})" 21 | ``` 22 | 23 | ```bash 24 | coretemp-isa-0000: coretemp 25 | Package id 0: 40.0 26 | Core 0: 39.0 27 | Core 1: 40.0 28 | thinkpad-isa-0000: thinkpad 29 | CPU: 34.0 30 | GPU: 0.0 31 | temp3: 0.0 32 | temp4: 0.0 33 | temp5: 0.0 34 | temp6: 0.0 35 | temp7: 0.0 36 | temp8: 0.0 37 | nvme-pci-0500: nvme 38 | Composite: 23.85 39 | BAT0-acpi-0: BAT0 40 | iwlwifi_1-virtual-0: iwlwifi_1 41 | temp1: 28.0 42 | pch_skylake-virtual-0: pch_skylake 43 | temp1: 33.0 44 | BAT1-acpi-0: BAT1 45 | acpitz-acpi-0: acpitz 46 | temp1: 34.0 47 | ``` 48 | 49 | #### Helpers example 50 | ```nim 51 | import sensors 52 | 53 | init() 54 | 55 | echo cpuTemp() 56 | echo ssdTemp() 57 | ``` 58 | -------------------------------------------------------------------------------- /src/sensors.nim: -------------------------------------------------------------------------------- 1 | import sensors/bindings 2 | 3 | import std/dynlib 4 | import system/ansi_c 5 | 6 | export SensorFeatureKind, SensorSubfeatureKind 7 | 8 | type 9 | SensorFeature* = object 10 | feature: SensorFeaturePtr 11 | chip: SensorChip 12 | SensorSubfeature* = object 13 | subfeature: SensorSubfeaturePtr 14 | chip: SensorChip 15 | SensorsException* = object of ValueError 16 | errCode*: int 17 | 18 | proc newSensorsException*(err: int, msg = ""): ref SensorsException = 19 | new(result) 20 | result.errCode = err 21 | result.msg = msg & " with error code " & $err 22 | 23 | template checkErr*(body: untyped): untyped = 24 | let err = body 25 | if err != 0: 26 | raise newSensorsException(err, "sensors failed") 27 | 28 | proc init*() = 29 | initLib() 30 | checkErr sensors_init(nil) 31 | 32 | proc name*(chip: SensorChip): string = 33 | result.setLen 255 34 | result.setLen sensors_snprintf_chip_name(result[0].addr, len(result), chip) 35 | 36 | iterator chips*(): SensorChip = 37 | var n = 0 38 | while true: 39 | let chip = sensors_get_detected_chips(nil, n) 40 | if chip == nil: 41 | break 42 | yield chip 43 | 44 | proc chipWithPrefix*(prefix: string): SensorChip = 45 | for chip in chips(): 46 | if chip.prefix == prefix: 47 | return chip 48 | raise newException(KeyError, "chip with prefix `" & prefix & "` not found") 49 | 50 | proc name*(feature: SensorFeature): cstring = 51 | feature.feature.name 52 | 53 | proc kind*(feature: SensorFeature): SensorFeatureKind = 54 | feature.feature.kind 55 | 56 | proc label*(feature: SensorFeature): string = 57 | var cstr = sensors_get_label(feature.chip, feature.feature) 58 | result = $cstr 59 | c_free(cstr) 60 | 61 | iterator features*(chip: SensorChip): SensorFeature = 62 | var n = 0 63 | while true: 64 | let feature = sensors_get_features(chip, n) 65 | if feature == nil: 66 | break 67 | yield SensorFeature(feature: feature, chip: chip) 68 | 69 | iterator features*(chip: SensorChip, kind: SensorFeatureKind): SensorFeature = 70 | for feature in chip.features(): 71 | if feature.kind != kind: 72 | continue 73 | yield feature 74 | 75 | iterator items*(chip: SensorChip): SensorFeature = 76 | for f in chip.features(): yield f 77 | 78 | proc feature*(chip: SensorChip, kind: SensorFeatureKind): SensorFeature = 79 | for feature in chip.features(): 80 | if feature.kind == kind: 81 | return feature 82 | raise newException(KeyError, "feature with kind `" & $kind & "` not found") 83 | 84 | proc subfeature*(feature: SensorFeature, 85 | kind: SensorSubfeatureKind): SensorSubfeature = 86 | result.subfeature = sensors_get_subfeature(feature.chip, feature.feature, kind) 87 | if result.subfeature == nil: 88 | raise newException(KeyError, "subfeature with kind `" & $kind & "` not found") 89 | result.chip = feature.chip 90 | 91 | iterator subfeatures*(feature: SensorFeature): SensorSubfeature = 92 | var n = 0 93 | while true: 94 | let subfeature = sensors_get_all_subfeatures(feature.chip, feature.feature, n) 95 | if subfeature == nil: 96 | break 97 | yield SensorSubfeature(subfeature: subfeature, chip: feature.chip) 98 | 99 | iterator items*(feature: SensorFeature): SensorSubFeature = 100 | for sf in feature.subfeatures(): yield sf 101 | 102 | proc name*(subfeature: SensorSubfeature): cstring = 103 | subfeature.subfeature.name 104 | 105 | proc kind*(subfeature: SensorSubFeature): SensorSubfeatureKind = 106 | subfeature.subfeature.kind 107 | 108 | iterator subfeatures*(feature: SensorFeature, 109 | kind: SensorSubfeatureKind): SensorSubfeature = 110 | for subfeature in feature.subfeatures(): 111 | if subfeature.kind != kind: 112 | continue 113 | yield subfeature 114 | 115 | proc value*(subfeature: SensorSubfeature): float64 = 116 | if sensors_get_value(subfeature.chip, subfeature.subfeature.number, result) < 0: 117 | raise newException(ValueError, "sensor value failed") 118 | 119 | proc cpuMaxTemp*(): float64 = 120 | for feature in chipWithPrefix("coretemp").features(FeatureTemp): 121 | let tmp = feature.subfeature(SubfeatureTempInput).value 122 | if tmp > result: 123 | result = tmp 124 | 125 | proc nvmeMaxTemp*(): float64 = 126 | for feature in chipWithPrefix("nvme").features(FeatureTemp): 127 | let tmp = feature.subfeature(SubfeatureTempInput).value 128 | if tmp > result: 129 | result = tmp 130 | -------------------------------------------------------------------------------- /src/sensors/bindings.nim: -------------------------------------------------------------------------------- 1 | type 2 | Bus* = object 3 | typ*, nr*: int16 4 | SensorChipRef* = ref object 5 | prefix*: cstring 6 | bus*: Bus 7 | addrr*: int32 8 | path*: cstring 9 | SensorChip* = ptr object 10 | prefix*: cstring 11 | bus*: Bus 12 | addrr*: int32 13 | path*: cstring 14 | type 15 | SensorFeatureKind* {.size: sizeof(cint), pure.} = enum 16 | FEATURE_IN = 0x00, FEATURE_FAN = 0x01, 17 | FEATURE_TEMP = 0x02, FEATURE_POWER = 0x03, 18 | FEATURE_ENERGY = 0x04, FEATURE_CURR = 0x05, 19 | FEATURE_HUMIDITY = 0x06, FEATURE_MAX_MAIN, 20 | FEATURE_VID = 0x10, FEATURE_INTRUSION = 0x11, 21 | FEATURE_MAX_OTHER, FEATURE_BEEP_ENABLE = 0x18, 22 | FEATURE_MAX, FEATURE_UNKNOWN = cint.high 23 | SensorSubfeatureKind* {.size: sizeof(cint), pure.} = enum 24 | SUBFEATURE_IN_INPUT = FEATURE_IN.cint shl 8, 25 | SUBFEATURE_IN_MIN, SUBFEATURE_IN_MAX, 26 | SUBFEATURE_IN_LCRIT, SUBFEATURE_IN_CRIT, 27 | SUBFEATURE_IN_AVERAGE, SUBFEATURE_IN_LOWEST, 28 | SUBFEATURE_IN_HIGHEST, 29 | SUBFEATURE_IN_ALARM = (FEATURE_IN.cint shl 8) or 0x80, 30 | SUBFEATURE_IN_MIN_ALARM, SUBFEATURE_IN_MAX_ALARM, 31 | SUBFEATURE_IN_BEEP, SUBFEATURE_IN_LCRIT_ALARM, 32 | SUBFEATURE_IN_CRIT_ALARM, 33 | SUBFEATURE_FAN_INPUT = FEATURE_FAN.cint shl 8, 34 | SUBFEATURE_FAN_MIN, SUBFEATURE_FAN_MAX, 35 | SUBFEATURE_FAN_ALARM = (FEATURE_FAN.cint shl 8) or 0x80, 36 | SUBFEATURE_FAN_FAULT, SUBFEATURE_FAN_DIV, 37 | SUBFEATURE_FAN_BEEP, SUBFEATURE_FAN_PULSES, 38 | SUBFEATURE_FAN_MIN_ALARM, SUBFEATURE_FAN_MAX_ALARM, 39 | SUBFEATURE_TEMP_INPUT = FEATURE_TEMP.cint shl 8, 40 | SUBFEATURE_TEMP_MAX, SUBFEATURE_TEMP_MAX_HYST, 41 | SUBFEATURE_TEMP_MIN, SUBFEATURE_TEMP_CRIT, 42 | SUBFEATURE_TEMP_CRIT_HYST, SUBFEATURE_TEMP_LCRIT, 43 | SUBFEATURE_TEMP_EMERGENCY, SUBFEATURE_TEMP_EMERGENCY_HYST, 44 | SUBFEATURE_TEMP_LOWEST, SUBFEATURE_TEMP_HIGHEST, 45 | SUBFEATURE_TEMP_MIN_HYST, SUBFEATURE_TEMP_LCRIT_HYST, 46 | SUBFEATURE_TEMP_ALARM = (FEATURE_TEMP.cint shl 8) or 0x80, 47 | SUBFEATURE_TEMP_MAX_ALARM, SUBFEATURE_TEMP_MIN_ALARM, 48 | SUBFEATURE_TEMP_CRIT_ALARM, SUBFEATURE_TEMP_FAULT, 49 | SUBFEATURE_TEMP_TYPE, SUBFEATURE_TEMP_OFFSET, 50 | SUBFEATURE_TEMP_BEEP, SUBFEATURE_TEMP_EMERGENCY_ALARM, 51 | SUBFEATURE_TEMP_LCRIT_ALARM, 52 | SUBFEATURE_POWER_AVERAGE = FEATURE_POWER.cint shl 8, 53 | SUBFEATURE_POWER_AVERAGE_HIGHEST, 54 | SUBFEATURE_POWER_AVERAGE_LOWEST, SUBFEATURE_POWER_INPUT, 55 | SUBFEATURE_POWER_INPUT_HIGHEST, 56 | SUBFEATURE_POWER_INPUT_LOWEST, SUBFEATURE_POWER_CAP, 57 | SUBFEATURE_POWER_CAP_HYST, SUBFEATURE_POWER_MAX, 58 | SUBFEATURE_POWER_CRIT, SUBFEATURE_POWER_MIN, 59 | SUBFEATURE_POWER_LCRIT, SUBFEATURE_POWER_AVERAGE_INTERVAL = ( 60 | FEATURE_POWER.cint shl 8) or 0x80, SUBFEATURE_POWER_ALARM, 61 | SUBFEATURE_POWER_CAP_ALARM, SUBFEATURE_POWER_MAX_ALARM, 62 | SUBFEATURE_POWER_CRIT_ALARM, SUBFEATURE_POWER_MIN_ALARM, 63 | SUBFEATURE_POWER_LCRIT_ALARM, 64 | SUBFEATURE_ENERGY_INPUT = FEATURE_ENERGY.cint shl 8, 65 | SUBFEATURE_CURR_INPUT = FEATURE_CURR.cint shl 8, 66 | SUBFEATURE_CURR_MIN, SUBFEATURE_CURR_MAX, 67 | SUBFEATURE_CURR_LCRIT, SUBFEATURE_CURR_CRIT, 68 | SUBFEATURE_CURR_AVERAGE, SUBFEATURE_CURR_LOWEST, 69 | SUBFEATURE_CURR_HIGHEST, 70 | SUBFEATURE_CURR_ALARM = (FEATURE_CURR.cint shl 8) or 0x80, 71 | SUBFEATURE_CURR_MIN_ALARM, SUBFEATURE_CURR_MAX_ALARM, 72 | SUBFEATURE_CURR_BEEP, SUBFEATURE_CURR_LCRIT_ALARM, 73 | SUBFEATURE_CURR_CRIT_ALARM, 74 | SUBFEATURE_HUMIDITY_INPUT = FEATURE_HUMIDITY.cint shl 8, 75 | SUBFEATURE_VID = FEATURE_VID.cint shl 8, 76 | SUBFEATURE_INTRUSION_ALARM = FEATURE_INTRUSION.cint shl 8, 77 | SUBFEATURE_INTRUSION_BEEP, 78 | SUBFEATURE_BEEP_ENABLE = FEATURE_BEEP_ENABLE.cint shl 8, 79 | SUBFEATURE_UNKNOWN = cint.high 80 | SensorFeaturePtr* = ptr object 81 | name*: cstring 82 | number*: int32 83 | kind*: SensorFeatureKind 84 | first_subfeature*: int32 85 | padding1*: int32 86 | SensorSubfeaturePtr* = ptr object 87 | name*: cstring 88 | number*: int32 89 | kind*: SensorSubfeatureKind 90 | mapping*: int32 91 | flags*: uint32 92 | 93 | const staticSensorsPath {.strdefine.}: string = "" 94 | 95 | when staticSensorsPath != "": 96 | {.passL: "-L"&staticSensorsPath.} 97 | {.passL: "-lsensors".} 98 | 99 | proc sensors_init*(p: typeof(nil)): int {.importc.} 100 | proc sensors_get_detected_chips*(p: SensorChip, 101 | nr: var int): SensorChip {.importc.} 102 | proc sensors_snprintf_chip_name*(buf: pointer, size: int, 103 | chip: SensorChip): int {.importc.} 104 | proc sensors_get_features*(chip: SensorChip, 105 | fnr: var int): SensorFeaturePtr {.importc.} 106 | proc sensors_get_label*(chip: SensorChip, 107 | feature: SensorFeaturePtr): cstring {.importc.} 108 | proc sensors_get_subfeature*(chip: SensorChip, feature: SensorFeaturePtr, 109 | kind: SensorSubfeatureKind): SensorSubfeaturePtr {.importc.} 110 | proc sensors_get_all_subfeatures*(chip: SensorChip, 111 | feature: SensorFeaturePtr, nr: var int): SensorSubfeaturePtr {.importc.} 112 | proc sensors_get_value*(chip: SensorChip, nr: int, 113 | value: var float64): int {.importc.} 114 | else: 115 | import std/dynlib 116 | 117 | const libSensorsPattern = "libsensors.(so|so.5)" 118 | 119 | var sensors_init*: proc(p: typeof(nil)): int {.cdecl.} 120 | var sensors_get_detected_chips*: proc(p: SensorChip, 121 | nr: var int): SensorChip {.cdecl.} 122 | var sensors_snprintf_chip_name*: proc(buf: pointer, size: int, 123 | chip: SensorChip): int {.cdecl.} 124 | var sensors_get_features*: proc(chip: SensorChip, 125 | fnr: var int): SensorFeaturePtr {.cdecl.} 126 | var sensors_get_label*: proc(chip: SensorChip, 127 | feature: SensorFeaturePtr): cstring {.cdecl.} 128 | var sensors_get_subfeature*: proc(chip: SensorChip, feature: SensorFeaturePtr, 129 | kind: SensorSubfeatureKind): SensorSubfeaturePtr {.cdecl.} 130 | var sensors_get_all_subfeatures*: proc(chip: SensorChip, 131 | feature: SensorFeaturePtr, nr: var int): SensorSubfeaturePtr {.cdecl.} 132 | var sensors_get_value*: proc(chip: SensorChip, nr: int, 133 | value: var float64): int {.cdecl.} 134 | 135 | template setProc(name: untyped) = 136 | name = cast[typeof(name)](lib.symAddr(name.astToStr)) 137 | doAssert name != nil 138 | 139 | proc initLib*() = 140 | when staticSensorsPath != "": 141 | discard 142 | else: 143 | let lib = loadLibPattern(libSensorsPattern) 144 | if lib == nil: 145 | raise newException(LibraryError, libSensorsPattern) 146 | setProc(sensors_init) 147 | setProc(sensors_get_detected_chips) 148 | setProc(sensors_snprintf_chip_name) 149 | setProc(sensors_get_features) 150 | setProc(sensors_get_label) 151 | setProc(sensors_get_subfeature) 152 | setProc(sensors_get_all_subfeatures) 153 | setProc(sensors_get_value) 154 | 155 | --------------------------------------------------------------------------------