ohmu
·
2026-10-15
1#include "battery.h"
2#include "common/debug.h"
3#include "common/io.h"
4#include "common/FFstrbuf.h"
5#include "common/strutil.h"
6
7#include <prop/prop_array.h>
8#include <prop/prop_bool.h>
9#include <prop/prop_dictionary.h>
10#include <prop/prop_object.h>
11#include <sys/envsys.h>
12#include <prop/proplib.h>
13#include <paths.h>
14#include <time.h>
15#include <unistd.h>
16#include <fcntl.h>
17
18const char* ffDetectBattery([[maybe_unused]] FFBatteryOptions* options, FFlist* results) {
19 FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC);
20 if (fd < 0) {
21 FF_DEBUG("open(_PATH_SYSMON) failed: %s", strerror(errno));
22 return "open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC) failed";
23 }
24
25 prop_dictionary_t root = nullptr;
26 if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0) {
27 FF_DEBUG("prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed");
28 return "prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed";
29 }
30
31 bool acConnected = false;
32 {
33 prop_array_t acad = prop_dictionary_get(root, "acpiacad0");
34 if (acad) {
35 prop_dictionary_t dict = prop_array_get(acad, 0);
36 prop_dictionary_get_uint8(dict, "cur-value", (uint8_t*) &acConnected);
37 }
38 }
39
40 prop_object_iterator_t itKey = prop_dictionary_iterator(root);
41 for (prop_dictionary_keysym_t key; (key = prop_object_iterator_next(itKey)) != nullptr;) {
42 if (!ffStrStartsWith(prop_dictionary_keysym_value(key), "acpibat")) {
43 continue;
44 }
45
46 prop_array_t bat = prop_dictionary_get_keysym(root, key);
47 uint32_t max = 0, curr = 0, dischargeRate = 0;
48 bool charging = false, critical = false;
49 prop_object_iterator_t iter = prop_array_iterator(bat);
50 for (prop_dictionary_t dict; (dict = prop_object_iterator_next(iter)) != nullptr;) {
51 if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) {
52 continue;
53 }
54
55 const char* desc = nullptr;
56 if (!prop_dictionary_get_string(dict, "description", &desc)) {
57 continue;
58 }
59
60 if (ffStrEquals(desc, "present")) {
61 int value = 0;
62 if (prop_dictionary_get_int(dict, "cur-value", &value) && value == 0) {
63 continue;
64 }
65 } else if (ffStrEquals(desc, "charging")) {
66 prop_dictionary_get_uint8(dict, "cur-value", (uint8_t*) &charging);
67 } else if (ffStrEquals(desc, "charge")) {
68 prop_dictionary_get_uint32(dict, "max-value", &max);
69 prop_dictionary_get_uint32(dict, "cur-value", &curr);
70 const char* state = nullptr;
71 if (prop_dictionary_get_string(dict, "state", &state) && ffStrEquals(state, "critical")) {
72 critical = true;
73 }
74 } else if (ffStrEquals(desc, "discharge rate")) {
75 prop_dictionary_get_uint(dict, "cur-value", &dischargeRate);
76 }
77 }
78
79 if (max > 0) {
80 FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
81 battery->temperature = FF_BATTERY_TEMP_UNSET;
82 battery->cycleCount = 0;
83 ffStrbufInit(&battery->manufacturer);
84 ffStrbufInit(&battery->modelName);
85 ffStrbufInit(&battery->technology);
86 ffStrbufInit(&battery->serial);
87 ffStrbufInit(&battery->manufactureDate);
88 battery->status = FF_BATTERY_STATUS_NONE;
89 battery->timeRemaining = -1;
90
91 battery->capacity = (double) curr / (double) max * 100.;
92 if (charging) {
93 battery->status |= FF_BATTERY_STATUS_CHARGING;
94 } else if (dischargeRate) {
95 battery->status |= FF_BATTERY_STATUS_DISCHARGING;
96 battery->timeRemaining = (int32_t) ((double) curr / dischargeRate * 3600);
97 }
98 if (critical) {
99 battery->status |= FF_BATTERY_STATUS_CRITICAL;
100 }
101 if (acConnected) {
102 battery->status |= FF_BATTERY_STATUS_AC_CONNECTED;
103 }
104 }
105
106 prop_object_iterator_release(iter);
107 }
108 prop_object_iterator_release(itKey);
109 prop_object_release(root);
110
111 return nullptr;
112}