Skip to content

Commit 40b7a13

Browse files
Merge pull request #3 from CCExtractor/dev-api-expansion
[FEATURE] Expose more ectool functions in pyectool
2 parents 99802e7 + 56e1c71 commit 40b7a13

File tree

7 files changed

+953
-55
lines changed

7 files changed

+953
-55
lines changed

pyectool/__init__.pyi

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,37 @@ from __future__ import annotations
33
__doc__: str
44
__version__: str
55

6+
class ECTempInfo(dict[str, int | str]):
7+
sensor_name: str
8+
sensor_type: int
9+
temp: int
10+
temp_fan_off: int
11+
temp_fan_max: int
12+
13+
class ECChargeStateInfo(dict[str, int]):
14+
ac: int
15+
chg_voltage: int
16+
chg_current: int
17+
chg_input_current: int
18+
batt_state_of_charge: int
19+
620
class ECController:
721
def __init__(self) -> None: ...
22+
def hello(self) -> None: ...
823
def is_on_ac(self) -> bool: ...
9-
def auto_fan_control(self) -> None: ...
10-
def set_fan_duty(self, duty: int) -> None: ...
11-
def get_max_temperature(self) -> float: ...
12-
def get_max_non_battery_temperature(self) -> float: ...
24+
def get_charge_state(self) -> ECChargeStateInfo: ...
25+
def get_num_fans(self) -> int: ...
26+
def enable_fan_auto_ctrl(self, fan_idx: int) -> None: ...
27+
def enable_all_fans_auto_ctrl(self) -> None: ...
28+
def set_fan_duty(self, percent: int, fan_idx: int) -> None: ...
29+
def set_all_fans_duty(self, percent: int) -> None: ...
30+
def set_fan_rpm(self, target_rpm: int, fan_idx: int) -> None: ...
31+
def set_all_fans_rpm(self, target_rpm: int) -> None: ...
32+
def get_fan_rpm(self, fan_idx: int) -> int: ...
33+
def get_all_fans_rpm(self) -> list[int]: ...
34+
def get_num_temp_sensors(self) -> int: ...
35+
def get_temp(self, sensor_idx: int) -> int: ...
36+
def get_all_temps(self) -> list[int]: ...
37+
def get_max_temp(self) -> int: ...
38+
def get_max_non_battery_temp(self) -> int: ...
39+
def get_temp_info(self, sensor_idx: int) -> ECTempInfo: ...

src/bindings/ECController.cc

Lines changed: 121 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,30 @@ void ECController::handle_error(int code, const std::string &msg) {
1010
case EC_ERR_READMEM: reason = "EC memory read failed"; break;
1111
case EC_ERR_EC_COMMAND: reason = "EC command failed"; break;
1212
case EC_ERR_INVALID_PARAM: reason = "Invalid parameter"; break;
13+
case EC_ERR_SENSOR_UNAVAILABLE:
14+
reason = "Sensor unavailable or not calibrated/powered";
15+
break;
16+
case EC_ERR_UNSUPPORTED_VER:
17+
reason = "Unsupported EC command version";
18+
break;
19+
20+
case EC_ERR_INVALID_RESPONSE:
21+
reason = "Invalid response from EC";
22+
break;
1323
default: reason = "Unknown error"; break;
1424
}
1525

1626
throw std::runtime_error(msg + " (" + reason + ", code " + std::to_string(code) + ")");
1727
}
1828

29+
int ECController::hello() {
30+
int ret = ec_hello();
31+
return ret;
32+
}
33+
34+
// -----------------------------------------------------------------------------
35+
// Top-level Power Functions
36+
// -----------------------------------------------------------------------------
1937

2038
bool ECController::is_on_ac() {
2139
int ac;
@@ -24,26 +42,116 @@ bool ECController::is_on_ac() {
2442
return ac;
2543
}
2644

27-
void ECController::auto_fan_control() {
28-
int ret = ec_auto_fan_control();
45+
ec_charge_state_info ECController::get_charge_state() {
46+
ec_charge_state_info info;
47+
int ret = ec_get_charge_state(&info);
48+
handle_error(ret, "Failed to get charge state");
49+
return info;
50+
}
51+
52+
// -----------------------------------------------------------------------------
53+
// Top-level fan control Functions
54+
// -----------------------------------------------------------------------------
55+
56+
int ECController::get_num_fans() {
57+
int val = 0;
58+
int ret = ec_get_num_fans(&val);
59+
handle_error(ret, "Failed to get number of fans");
60+
return val;
61+
}
62+
63+
void ECController::enable_fan_auto_ctrl(int fan_idx) {
64+
int ret = ec_enable_fan_auto_ctrl(fan_idx);
2965
handle_error(ret, "Failed to enable auto fan control");
3066
}
3167

32-
void ECController::set_fan_duty(int duty) {
33-
int ret = ec_set_fan_duty(duty);
68+
void ECController::enable_all_fans_auto_ctrl() {
69+
int ret = ec_enable_all_fans_auto_ctrl();
70+
handle_error(ret, "Failed to enable auto control for all fans");
71+
}
72+
73+
void ECController::set_fan_duty(int percent, int fan_idx) {
74+
int ret = ec_set_fan_duty(percent, fan_idx);
3475
handle_error(ret, "Failed to set fan duty");
3576
}
3677

37-
float ECController::get_max_temperature() {
38-
float t;
39-
int ret = ec_get_max_temperature(&t);
78+
void ECController::set_all_fans_duty(int percent) {
79+
int ret = ec_set_all_fans_duty(percent);
80+
handle_error(ret, "Failed to set duty for all fans");
81+
}
82+
83+
void ECController::set_fan_rpm(int target_rpm, int fan_idx) {
84+
int ret = ec_set_fan_rpm(target_rpm, fan_idx);
85+
handle_error(ret, "Failed to set fan RPM");
86+
}
87+
88+
void ECController::set_all_fans_rpm(int target_rpm) {
89+
int ret = ec_set_all_fans_rpm(target_rpm);
90+
handle_error(ret, "Failed to set RPM for all fans");
91+
}
92+
93+
int ECController::get_fan_rpm(int fan_idx) {
94+
int rpm = 0;
95+
int ret = ec_get_fan_rpm(&rpm, fan_idx);
96+
handle_error(ret, "Failed to get fan RPM");
97+
return rpm;
98+
}
99+
100+
std::vector<int> ECController::get_all_fans_rpm() {
101+
int num_fans = get_num_fans();
102+
std::vector<int> rpms(num_fans);
103+
int num_fans_out = 0;
104+
105+
int ret = ec_get_all_fans_rpm(rpms.data(), num_fans, &num_fans_out);
106+
handle_error(ret, "Failed to get all fan RPMs");
107+
return rpms;
108+
}
109+
110+
// -----------------------------------------------------------------------------
111+
// Top-level temperature Functions
112+
// -----------------------------------------------------------------------------
113+
int ECController::get_num_temp_sensors() {
114+
int val = 0;
115+
int ret = ec_get_num_temp_sensors(&val);
116+
handle_error(ret, "Failed to get number of temp sensors");
117+
return val;
118+
}
119+
120+
int ECController::get_temp(int sensor_idx) {
121+
int temp = 0;
122+
int ret = ec_get_temp(sensor_idx, &temp);
123+
handle_error(ret, "Failed to get temperature");
124+
return temp;
125+
}
126+
127+
std::vector<int> ECController::get_all_temps() {
128+
int max_entries = get_num_temp_sensors();
129+
std::vector<int> temps(max_entries);
130+
int num_sensors = 0;
131+
132+
int ret = ec_get_all_temps(temps.data(), max_entries, &num_sensors);
133+
handle_error(ret, "Failed to get all temperatures");
134+
temps.resize(num_sensors); // Trim unused entries
135+
return temps;
136+
}
137+
138+
int ECController::get_max_temp() {
139+
int temp = 0;
140+
int ret = ec_get_max_temp(&temp);
40141
handle_error(ret, "Failed to get max temperature");
41-
return t;
142+
return temp;
143+
}
144+
145+
int ECController::get_max_non_battery_temp() {
146+
int temp = 0;
147+
int ret = ec_get_max_non_battery_temp(&temp);
148+
handle_error(ret, "Failed to get max non-battery temperature");
149+
return temp;
42150
}
43151

44-
float ECController::get_max_non_battery_temperature() {
45-
float t;
46-
int ret = ec_get_max_non_battery_temperature(&t);
47-
handle_error(ret, "Failed to get non-battery temperature");
48-
return t;
152+
ec_temp_info ECController::get_temp_info(int sensor_idx) {
153+
ec_temp_info info;
154+
int ret = ec_get_temp_info(sensor_idx, &info);
155+
handle_error(ret, "Failed to get temp sensor info");
156+
return info;
49157
}

src/bindings/ECController.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
#pragma once
22
#include <stdexcept>
33
#include <string>
4+
#include <vector>
5+
#include "libectool.h"
46

57
class ECController {
68
public:
9+
int hello();
10+
711
bool is_on_ac();
8-
void auto_fan_control();
9-
void set_fan_duty(int duty);
10-
float get_max_temperature();
11-
float get_max_non_battery_temperature();
12+
ec_charge_state_info get_charge_state();
13+
14+
int get_num_fans();
15+
void enable_fan_auto_ctrl(int fan_idx);
16+
void enable_all_fans_auto_ctrl();
17+
void set_fan_duty(int percent, int fan_idx);
18+
void set_all_fans_duty(int percent);
19+
void set_fan_rpm(int target_rpm, int fan_idx);
20+
void set_all_fans_rpm(int target_rpm);
21+
int get_fan_rpm(int fan_idx);
22+
std::vector<int> get_all_fans_rpm();
23+
24+
int get_num_temp_sensors();
25+
int get_temp(int sensor_idx);
26+
std::vector<int> get_all_temps();
27+
int get_max_temp();
28+
int get_max_non_battery_temp();
29+
ec_temp_info get_temp_info(int sensor_idx);
1230

1331
private:
1432
void handle_error(int code, const std::string &msg);

src/bindings/PyECController.cc

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,120 @@
11
#include <pybind11/pybind11.h>
2+
#include <pybind11/stl.h>
23
#include "ECController.h"
4+
#include "libectool.h"
35

46
#define STRINGIFY(x) #x
57
#define MACRO_STRINGIFY(x) STRINGIFY(x)
68

79
namespace py = pybind11;
810

11+
py::dict temp_info_to_dict(const ec_temp_info& info) {
12+
py::dict d;
13+
d["sensor_name"] = std::string(info.sensor_name);
14+
d["sensor_type"] = info.sensor_type;
15+
d["temp"] = info.temp;
16+
d["temp_fan_off"] = info.temp_fan_off;
17+
d["temp_fan_max"] = info.temp_fan_max;
18+
return d;
19+
}
20+
21+
py::dict charge_state_to_dict(const ec_charge_state_info& info) {
22+
py::dict d;
23+
d["ac"] = static_cast<bool>(info.ac);
24+
d["chg_voltage"] = info.chg_voltage;
25+
d["chg_current"] = info.chg_current;
26+
d["chg_input_current"] = info.chg_input_current;
27+
d["batt_state_of_charge"] = info.batt_state_of_charge;
28+
return d;
29+
}
30+
31+
932
PYBIND11_MODULE(libectool_py, m) {
1033
m.doc() = "Python bindings for ectool";
1134

1235
py::class_<ECController>(m, "ECController")
13-
.def(py::init<>())
14-
.def("is_on_ac", &ECController::is_on_ac, "Check if on AC power")
15-
.def("auto_fan_control", &ECController::auto_fan_control, "Enable automatic fan control")
16-
.def("set_fan_duty", &ECController::set_fan_duty,
17-
"Set fan duty cycle (0-100)", py::arg("duty"))
18-
.def("get_max_temperature", &ECController::get_max_temperature,
19-
"Get max temperature")
20-
.def("get_max_non_battery_temperature",
21-
&ECController::get_max_non_battery_temperature,
22-
"Get max non-battery temperature");
36+
.def(py::init<>())
37+
.def("hello", &ECController::hello, "Send hello command to EC")
38+
39+
.def("is_on_ac", &ECController::is_on_ac, "Check if on AC power")
40+
41+
.def("get_charge_state", [](ECController& self) {
42+
return charge_state_to_dict(self.get_charge_state());
43+
}, "Get charge state info")
44+
45+
.def("get_num_fans", &ECController::get_num_fans,
46+
"Get number of fans")
47+
48+
.def("enable_fan_auto_ctrl",
49+
&ECController::enable_fan_auto_ctrl,
50+
"Enable auto control for a fan",
51+
py::arg("fan_idx"))
52+
53+
.def("enable_all_fans_auto_ctrl",
54+
&ECController::enable_all_fans_auto_ctrl,
55+
"Enable auto control for all fans")
56+
57+
.def("set_fan_duty",
58+
&ECController::set_fan_duty,
59+
"Set fan duty cycle (0-100)",
60+
py::arg("percent"), py::arg("fan_idx"))
61+
62+
.def("set_all_fans_duty",
63+
&ECController::set_all_fans_duty,
64+
"Set all fans duty cycle (0-100)",
65+
py::arg("percent"))
66+
67+
.def("set_fan_rpm",
68+
&ECController::set_fan_rpm,
69+
"Set fan RPM",
70+
py::arg("target_rpm"), py::arg("fan_idx"))
71+
72+
.def("set_all_fans_rpm",
73+
&ECController::set_all_fans_rpm,
74+
"Set all fans RPM",
75+
py::arg("target_rpm"))
76+
77+
.def("get_fan_rpm",
78+
&ECController::get_fan_rpm,
79+
"Get single fan RPM",
80+
py::arg("fan_idx"))
81+
82+
.def("get_all_fans_rpm",
83+
[](ECController &self) {
84+
return py::cast(self.get_all_fans_rpm());
85+
},
86+
"Get all fans RPM as list")
87+
88+
.def("get_num_temp_sensors",
89+
&ECController::get_num_temp_sensors,
90+
"Get number of temperature sensors")
91+
92+
.def("get_temp",
93+
&ECController::get_temp,
94+
"Get temperature in Celsius for one sensor",
95+
py::arg("sensor_idx"))
96+
97+
.def("get_all_temps",
98+
[](ECController &self) {
99+
return py::cast(self.get_all_temps());
100+
},
101+
"Get all temperature values as list")
102+
103+
.def("get_max_temp",
104+
&ECController::get_max_temp,
105+
"Get maximum temperature across all sensors")
106+
107+
.def("get_max_non_battery_temp",
108+
&ECController::get_max_non_battery_temp,
109+
"Get maximum non-battery temperature")
110+
111+
.def("get_temp_info",
112+
[](ECController &self, int sensor_idx) {
113+
ec_temp_info info = self.get_temp_info(sensor_idx);
114+
return temp_info_to_dict(info);
115+
},
116+
"Get detailed temperature info for a sensor",
117+
py::arg("sensor_idx"));
23118

24119
#ifdef VERSION_INFO
25120
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);

0 commit comments

Comments
 (0)