4 Commits

Author SHA1 Message Date
Tabascl
8ff1d8be9d Bump version numbers 2024-07-07 23:52:03 +02:00
Tabascl
78b2a62643 Remove aggregator, fix changing HWMON paths
Add logic to use actual device paths for the PWM sensors instead of their
standart HWMON paths.

This was done after noticing that due to the load order of kernel modules,
the index of HWMON paths could change.
2024-07-07 22:19:27 +02:00
e2509cea8b Various fixes
- Change unit path
- Rename method
- Make CurrentPWM method return its value in PWM instead of percent
2023-10-08 13:50:33 +02:00
0a6bab36be Hysteresis, Inhibit-Stop Timeframe 2023-08-28 14:55:07 +02:00
20 changed files with 242 additions and 101 deletions

View File

@@ -1,5 +1,5 @@
pkgname=fantasize
pkgver=0.2.0
pkgver=0.3.0
pkgrel=1
pkgdesc='C++ fan control for Linux'
url='https://github.com/Tabascl/fantasize.git'

View File

@@ -17,16 +17,16 @@
using json = nlohmann::json;
class Serializer
{
class Serializer {
public:
Serializer();
void SerializeFans(std::vector<std::shared_ptr<Fan>> fans);
std::vector<std::shared_ptr<Fan>> DeserializeFans(
std::vector<std::shared_ptr<Sensor>> availableSensors);
std::vector<std::shared_ptr<FanCurve>> DeserializeFanCurves(
std::vector<std::shared_ptr<Sensor>> availableSensors,
std::vector<std::shared_ptr<Fan>> availableFans);
std::vector<std::shared_ptr<Fan>>
DeserializeFans(std::vector<std::shared_ptr<Sensor>> availableSensors);
std::vector<std::shared_ptr<FanCurve>>
DeserializeFanCurves(std::vector<std::shared_ptr<Sensor>> availableSensors,
std::vector<std::shared_ptr<Fan>> availableFans,
bool &everythingFound);
std::shared_ptr<Settings> DeserializeSettings();
private:

View File

@@ -22,6 +22,8 @@ public:
virtual void FindPWMLimits() = 0;
virtual void AdjustPWMLimits() = 0;
virtual void EnforceSetValue() = 0;
};
#endif // FAN_H_

View File

@@ -8,30 +8,31 @@
#include <fan/HwmonFan.h>
#include <sensor/Sensor.h>
struct FanStep
{
struct FanStep {
int Temp;
int Percent;
};
class FanCurve
{
class FanCurve {
public:
FanCurve(std::vector<FanStep> steps,
std::vector<std::shared_ptr<Sensor>> sensors,
std::vector<std::shared_ptr<Fan>> fans,
std::unique_ptr<Aggregator> aggregator);
std::unique_ptr<Aggregator> aggregator, int hysteresis);
void DoFanControl();
private:
int AggregateTemperature();
void PrintInfo();
bool ExceedsHysteresis(int temperature);
void ApplyFanPower(std::shared_ptr<Fan> fan, int targetFanPower);
std::vector<FanStep> mSteps;
std::vector<std::shared_ptr<Sensor>> mTempSensors;
std::vector<std::shared_ptr<Fan>> mFans;
std::unique_ptr<Aggregator> mAggregator;
int mHystersis;
int mLastTemperature;
};
#endif // FANCURVE_H_

View File

@@ -30,11 +30,16 @@ public:
void FindPWMLimits() override;
void AdjustPWMLimits() override;
void EnforceSetValue() override;
json toJson() const override;
const std::string toString() const override;
private:
bool InhibitStopPeriodExpired();
void SetPower(int percent);
std::shared_ptr<PWMControl> mPWMControl;
std::shared_ptr<Sensor> mRpmSensor;
std::string mLabel;
@@ -43,6 +48,9 @@ private:
int mStartPWM = 0;
bool mZeroFanModeSupported = false;
std::chrono::time_point<std::chrono::steady_clock> mLastAdjustmentTime;
std::chrono::time_point<std::chrono::steady_clock> mLastStartTime;
int mSetValue = 0;
bool mWasStopped = false;
};
#endif // HWMONFAN_H_

View File

@@ -12,11 +12,11 @@ enum class PWM_MODE { DC = 0, PWM };
class PWMControl : public Printable, public Serializable {
public:
PWMControl(std::string controlPath);
PWMControl(std::string controlPath, int deviceIndex);
~PWMControl();
void Power(int percent);
int Power();
void SetPower(int percent);
int CurrentPWM();
void EnableManualControl();
void Reset();
@@ -26,11 +26,11 @@ public:
json toJson() const override;
private:
int mCurrentValue;
std::string mConfigPath;
std::string mControlPath;
std::string mEnablePath;
std::string mModePath;
int mDeviceIndex;
std::string mInitialEnable;
std::string mInitialMode;

View File

@@ -16,9 +16,13 @@ public:
std::vector<std::shared_ptr<Sensor>> TemperatureSensors();
std::vector<std::shared_ptr<Sensor>> RPMSensors();
void ReloadSensors();
private:
template <sensors_subfeature_type T>
std::vector<std::shared_ptr<Sensor>> Sensors();
void InitSensors();
void CleanupSensors();
private:
FILE *mConfigFile;

View File

@@ -12,6 +12,8 @@ public:
std::vector<std::shared_ptr<Sensor>> TemperatureSensors();
std::vector<std::shared_ptr<Sensor>> RPMSensors();
void ReloadSensors();
private:
std::unique_ptr<LMSensorsFacade> mLMSensorsFacade;
std::unique_ptr<GPUSensorsFacade> mGPUSensorsFacade;

View File

@@ -1,4 +1,4 @@
project('fantasize', 'cpp', version : '0.2.1', default_options : ['cpp_std=c++20'])
project('fantasize', 'cpp', version : '0.3.0', default_options : 'cpp_std=c++20')
src = [
'src/main.cxx',
@@ -34,4 +34,4 @@ exe = executable('fantasize',
install: true,
include_directories: inc)
install_data('unit/fantasize.service', install_dir : 'usr/lib/systemd/system')
install_data('unit/fantasize.service', install_dir : '/usr/lib/systemd/system')

View File

@@ -1,14 +1,35 @@
#include "fan/Fan.h"
#include "fan/FanCurve.h"
#include <chrono>
#include <execution>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <App.h>
#include <memory>
#include <thread>
using namespace std;
void App::Init() {
mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors());
BOOST_LOG_FUNCTION();
bool everythingFound = false;
mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors());
auto fanCurves = mSerializer.DeserializeFanCurves(
mSensorManager.TemperatureSensors(), mFans);
mSensorManager.TemperatureSensors(), mFans, everythingFound);
while (!everythingFound) {
BOOST_LOG_TRIVIAL(info) << "Couldn't find every sensor. Retrying in 5s...";
this_thread::sleep_for(chrono::seconds(5));
mSensorManager.ReloadSensors();
mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors());
fanCurves = mSerializer.DeserializeFanCurves(
mSensorManager.TemperatureSensors(), mFans, everythingFound);
}
mSettings = mSerializer.DeserializeSettings();

View File

@@ -8,7 +8,7 @@
#include <fan/HwmonFan.h>
#include <pwm/PWMControl.h>
#define SETTLE_TIMEOUT 5
#define SETTLE_TIMEOUT 30
using namespace std;
@@ -22,7 +22,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
cout << "Setting all fans to maximum speed" << endl;
for (auto c : pwmControls) {
c->EnableManualControl();
c->Power(100);
c->SetPower(100);
}
// Wait for fans to settle
@@ -41,7 +41,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
for (auto c : pwmControls) {
cout << "Setting " << c->toString()
<< " to 50% and wait for it to settle..." << endl;
c->Power(50);
c->SetPower(50);
this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT));
@@ -53,7 +53,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
}
cout << "Setting fan back to 100%" << endl;
c->Power(100);
c->SetPower(100);
}
return mapping;

View File

@@ -1,9 +1,11 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <Serializer.h>
#include <fan/HwmonFan.h>
#include <pwm/PWMControl.h>
@@ -31,9 +33,11 @@ void Serializer::SerializeFans(vector<shared_ptr<Fan>> fans) {
vector<shared_ptr<Fan>>
Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
BOOST_LOG_FUNCTION();
vector<shared_ptr<Fan>> fans;
// Create a for the sensors first, then searching becomes cheaper
// Create a map for the sensors first, then searching becomes cheaper
map<string, shared_ptr<Sensor>> sensorMap;
for (auto s : availableSensors) {
sensorMap[s->toString()] = s;
@@ -42,7 +46,7 @@ Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
auto data = ReadJson();
try {
for (auto &el : data["fans"].items()) {
auto pwmControl = make_shared<PWMControl>(el.value()["PWMControl"]);
auto pwmControl = make_shared<PWMControl>(el.value()["PWMControl"]["Path"], el.value()["PWMControl"]["Index"]);
auto rpmSensor = sensorMap[el.value()["LMSensor"]];
int minPWM = el.value()["MinPWM"];
@@ -61,6 +65,12 @@ Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
} catch (const std::exception &e) {
std::cout << "Deserialization error! Message: " << e.what() << std::endl;
}
BOOST_LOG_TRIVIAL(trace) << "### Available Fans";
for (auto f : fans) {
BOOST_LOG_TRIVIAL(trace) << f->toString();
}
return fans;
}
@@ -86,7 +96,10 @@ json Serializer::ReadJson() {
vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
std::vector<std::shared_ptr<Sensor>> availableSensors,
std::vector<std::shared_ptr<Fan>> availableFans) {
std::vector<std::shared_ptr<Fan>> availableFans, bool &everythingFound) {
BOOST_LOG_FUNCTION();
everythingFound = true;
auto data = ReadJson();
map<string, shared_ptr<Sensor>> sensorMap;
@@ -113,6 +126,11 @@ vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
for (auto &sensor : el.value()["Sensors"].items()) {
if (sensorMap.contains(sensor.value()))
sensors.push_back(sensorMap[sensor.value()]);
else {
BOOST_LOG_TRIVIAL(warning)
<< "Sensor " << sensor.value() << " not found!";
everythingFound = false;
}
}
for (auto &fan : el.value()["Fans"].items()) {
@@ -123,8 +141,10 @@ vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
std::unique_ptr<Aggregator> aggregator =
aggregatorFromString(el.value()["AggregateFunction"]);
curves.push_back(
make_shared<FanCurve>(steps, sensors, fans, std::move(aggregator)));
int hysteresis = el.value()["Hysteresis"];
curves.push_back(make_shared<FanCurve>(steps, sensors, fans,
std::move(aggregator), hysteresis));
}
return curves;

View File

@@ -1,6 +1,6 @@
#include <boost/log/attributes/named_scope.hpp>
#include <iostream>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <fan/FanCurve.h>
@@ -11,16 +11,25 @@ using namespace std;
FanCurve::FanCurve(std::vector<FanStep> steps,
std::vector<std::shared_ptr<Sensor>> sensors,
std::vector<std::shared_ptr<Fan>> fans,
std::unique_ptr<Aggregator> aggregator)
std::unique_ptr<Aggregator> aggregator, int hysteresis)
: mSteps(steps), mTempSensors(sensors), mFans(fans),
mAggregator(std::move(aggregator)) {
mAggregator(std::move(aggregator)), mHystersis(hysteresis),
mLastTemperature(INT_MIN) {
PrintInfo();
}
void FanCurve::DoFanControl() {
BOOST_LOG_FUNCTION();
BOOST_LOG_FUNCTION()
int temp = AggregateTemperature();
BOOST_LOG_TRIVIAL(trace) << "## Fans in curve";
for (auto f : mFans)
BOOST_LOG_TRIVIAL(trace) << f->toString();
BOOST_LOG_TRIVIAL(trace) << "## Sensors in curve";
for (auto s : mTempSensors)
BOOST_LOG_TRIVIAL(trace) << s->toString();
int temp = mAggregator->aggregate(mTempSensors);
int t0 = 0, t1 = 0, p0 = 0, p1 = 0;
int targetFanPower;
@@ -43,19 +52,23 @@ void FanCurve::DoFanControl() {
targetFanPower = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0);
}
for (auto f : mFans) {
if (!f->ZeroFanModeSupported() && f->RPM() <= 0) {
BOOST_LOG_TRIVIAL(warning) << "Fan stopped completely!";
f->PWM(f->StartPWM());
f->AdjustPWMLimits();
} else {
f->PWM(targetFanPower);
}
}
}
BOOST_LOG_TRIVIAL(trace) << "Current temp: " << temp;
BOOST_LOG_TRIVIAL(trace) << "Last temp: " << mLastTemperature;
int FanCurve::AggregateTemperature() {
return mAggregator->aggregate(mTempSensors);
BOOST_LOG_TRIVIAL(trace) << "# Hysteresis check";
if (ExceedsHysteresis(temp)) {
BOOST_LOG_TRIVIAL(trace) << "passed";
for (auto f : mFans) {
ApplyFanPower(f, targetFanPower);
mLastTemperature = temp;
}
} else {
for (auto f : mFans)
f->EnforceSetValue();
BOOST_LOG_TRIVIAL(trace) << "not passed";
}
}
void FanCurve::PrintInfo() {
@@ -94,3 +107,22 @@ void FanCurve::PrintInfo() {
BOOST_LOG_TRIVIAL(info) << sStream.str();
}
bool FanCurve::ExceedsHysteresis(int temperature) {
int lowThreshold = mLastTemperature - mHystersis;
int highThreshold = mLastTemperature + mHystersis;
return temperature <= lowThreshold || temperature >= highThreshold;
}
void FanCurve::ApplyFanPower(std::shared_ptr<Fan> fan, int targetFanPower) {
BOOST_LOG_FUNCTION();
if (!fan->ZeroFanModeSupported() && fan->RPM() <= 0) {
BOOST_LOG_TRIVIAL(warning) << "Fan " << fan->toString() << " stopped completely!";
fan->PWM(fan->StartPWM());
fan->AdjustPWMLimits();
} else {
fan->PWM(targetFanPower);
}
}

View File

@@ -16,7 +16,7 @@ void FanLabeler::RunFanLabelInteraction(
cout << endl;
for (auto f : fans) {
cout << "Setting fan to max power" << endl;
cout << "Setting fan " << f->toString() << " to max power" << endl;
f->PWM(100);

View File

@@ -1,3 +1,4 @@
#include <chrono>
#include <iostream>
#include <ostream>
#include <thread>
@@ -9,6 +10,7 @@
#include <pwm/PWMControl.h>
#define TIMEOUT 10
#define INHIBIT_STOP_PERIOD 120
#define STEP 2
using namespace std;
@@ -21,12 +23,40 @@ HwmonFan::HwmonFan(std::shared_ptr<PWMControl> pwmControl,
void HwmonFan::PWM(int percent) {
if (percent < mMinPWM) {
mPWMControl->Power(mMinPWM);
if (mZeroFanModeSupported && InhibitStopPeriodExpired()) {
SetPower(percent);
mWasStopped = true;
} else {
SetPower(mMinPWM);
}
} else {
mPWMControl->Power(percent);
if (mWasStopped) {
mWasStopped = false;
mLastStartTime = chrono::steady_clock::now();
SetPower(mStartPWM);
} else {
SetPower(percent);
}
}
}
bool HwmonFan::InhibitStopPeriodExpired() {
BOOST_LOG_FUNCTION();
auto result = chrono::steady_clock::now() - mLastStartTime >
chrono::duration(chrono::seconds(INHIBIT_STOP_PERIOD));
BOOST_LOG_TRIVIAL(trace) << "Inhibit-Stop period expired:"
<< (result ? "true" : "false");
return result;
}
void HwmonFan::SetPower(int percent) {
mPWMControl->SetPower(percent);
mSetValue = percent;
}
int HwmonFan::RPM() { return mRpmSensor->value(); }
void HwmonFan::Label(std::string label) { mLabel = label; }
@@ -99,6 +129,8 @@ void HwmonFan::AdjustPWMLimits() {
}
}
void HwmonFan::EnforceSetValue() { mPWMControl->SetPower(mSetValue); }
json HwmonFan::toJson() const {
json obj;
obj = {mPWMControl->toJson(), mRpmSensor->toJson(),
@@ -111,6 +143,6 @@ const string HwmonFan::toString() const {
if (!mLabel.empty()) {
return mLabel;
} else {
return "fan:" + mPWMControl->toString();
return mPWMControl->toString();
}
}

View File

@@ -20,15 +20,13 @@
#include <App.h>
#define PROJECT_VERSION "v0.2.1"
#define PROJECT_VERSION "v0.3.0"
namespace po = boost::program_options;
namespace logging = boost::log;
App app;
static int doInitialSetup = 0;
void signal_handler(int s) { app.Shutdown(); }
void InitLogging(bool verbose) {

View File

@@ -1,8 +1,8 @@
#include <boost/log/attributes/named_scope.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <pwm/PWMControl.h>
@@ -15,19 +15,27 @@
using namespace std;
namespace fs = filesystem;
PWMControl::PWMControl(string controlPath)
: mControlPath(controlPath)
{
fs::path pathEnable(mControlPath + PWM_POSTFIX_ENABLE);
fs::path pathMode(mControlPath + PWM_POSTFIX_MODE);
PWMControl::PWMControl(string controlPath, int deviceIndex)
: mConfigPath(controlPath), mDeviceIndex(deviceIndex) {
auto path = fs::path{controlPath};
mEnablePath = pathEnable;
mModePath = pathMode;
int fileCount =
distance(fs::directory_iterator(path), fs::directory_iterator{});
if (fileCount != 1)
throw runtime_error("More than one HWMON device present, unsupported");
for (auto de : fs::directory_iterator(path)) {
auto testPath = de.path();
mControlPath =
fs::path{de.path() / (string("pwm").append(to_string(deviceIndex)))}
.string();
}
mEnablePath = fs::path{mControlPath + PWM_POSTFIX_ENABLE}.string();
mModePath = fs::path{mControlPath + PWM_POSTFIX_MODE}.string();
ifstream istrm;
mCurrentValue = Power();
istrm.open(mEnablePath);
istrm >> mInitialEnable;
istrm.close();
@@ -37,55 +45,44 @@ PWMControl::PWMControl(string controlPath)
istrm.close();
}
PWMControl::~PWMControl()
{
PWMControl::~PWMControl() {
BOOST_LOG_FUNCTION();
BOOST_LOG_TRIVIAL(trace) << "Cleanup";
Reset();
}
void
PWMControl::Power(int percent)
{
void PWMControl::SetPower(int percent) {
BOOST_LOG_FUNCTION();
int pwmValue = (PWM_MAX_VALUE * percent) / 100;
if (percent != mCurrentValue) {
if (pwmValue != CurrentPWM()) {
BOOST_LOG_TRIVIAL(trace) << "Updating control value of " << toString()
<< " to " << percent << "% (" << pwmValue << ")";
ofstream ostrm(mControlPath, ios::trunc);
ostrm << pwmValue;
ostrm.close();
mCurrentValue = percent;
}
}
int
PWMControl::Power()
{
int PWMControl::CurrentPWM() {
int value;
ifstream istrm;
istrm.open(mControlPath);
istrm >> value;
return (value * 100) / PWM_MAX_VALUE;
return value;
}
void
PWMControl::EnableManualControl()
{
void PWMControl::EnableManualControl() {
ofstream ostrm(mEnablePath, ios::trunc);
ostrm << static_cast<int>(PWM_ENABLE::MANUAL_CONTROL);
ostrm.close();
}
void
PWMControl::Reset()
{
void PWMControl::Reset() {
ofstream ostrm(mEnablePath, ios::trunc);
ostrm << mInitialEnable;
@@ -97,15 +94,9 @@ PWMControl::Reset()
ostrm.close();
}
const string
PWMControl::toString() const
{
return fs::path(mControlPath).filename();
}
const string PWMControl::toString() const { return fs::path(mControlPath); }
json
PWMControl::toJson() const
{
json obj = { "PWMControl", mControlPath };
json PWMControl::toJson() const {
json obj = {"PWMControl", {{"Path", mConfigPath}, {"Index", mDeviceIndex}}};
return obj;
}

View File

@@ -1,6 +1,7 @@
#include <filesystem>
#include <iostream>
#include <regex>
#include <string>
#include <pwm/PWMControlFacade.h>
@@ -20,14 +21,20 @@ vector<shared_ptr<PWMControl>> PWMControlFacade::PWMControls() {
for (const fs::directory_entry &hwmon_device :
fs::directory_iterator{HWMON_BASE_PATH}) {
// Resolve symlink to get device path instead of hwmon path, the latter of
// which can change
fs::path actual_path =
fs::canonical(HWMON_BASE_PATH / fs::read_symlink(hwmon_device));
for (const fs::directory_entry &control :
fs::directory_iterator{hwmon_device}) {
fs::directory_iterator{actual_path}) {
auto filename = control.path().filename().string();
if (regex_match(filename, re_ctrl)) {
auto controlPath = control.path().string();
auto controlIndex = filename.back() - '0';
auto controlPath = actual_path.parent_path();
controls.push_back(make_shared<PWMControl>(controlPath));
controls.push_back(make_shared<PWMControl>(controlPath, controlIndex));
}
}
}

View File

@@ -12,12 +12,10 @@ using namespace std;
#define CONFIG_FILE "/etc/conf.d/sensors"
LMSensorsFacade::LMSensorsFacade() : mConfigFile(fopen(CONFIG_FILE, "r")) {
if (sensors_init(mConfigFile) != 0) {
throw runtime_error("Config file doesn't exist");
}
InitSensors();
}
LMSensorsFacade::~LMSensorsFacade() { sensors_cleanup(); }
LMSensorsFacade::~LMSensorsFacade() { CleanupSensors(); }
std::vector<std::shared_ptr<Sensor>> LMSensorsFacade::TemperatureSensors() {
return Sensors<SENSORS_SUBFEATURE_TEMP_INPUT>();
@@ -48,3 +46,16 @@ std::vector<std::shared_ptr<Sensor>> LMSensorsFacade::Sensors() {
return sensors;
}
void LMSensorsFacade::ReloadSensors() {
CleanupSensors();
InitSensors();
}
void LMSensorsFacade::InitSensors() {
if (sensors_init(mConfigFile) != 0) {
throw runtime_error("Config file doesn't exist");
}
}
void LMSensorsFacade::CleanupSensors() { sensors_cleanup(); }

View File

@@ -1,5 +1,8 @@
#include <sensor/SensorManager.h>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
using namespace std;
SensorManager::SensorManager()
@@ -7,10 +10,17 @@ SensorManager::SensorManager()
mGPUSensorsFacade(make_unique<GPUSensorsFacade>()) {}
vector<shared_ptr<Sensor>> SensorManager::TemperatureSensors() {
BOOST_LOG_FUNCTION();
vector<shared_ptr<Sensor>> tempSensors;
tempSensors = mLMSensorsFacade->TemperatureSensors();
BOOST_LOG_TRIVIAL(trace) << "### Temperature sensors:";
for (auto s : tempSensors) {
BOOST_LOG_TRIVIAL(trace) << s->toString();
}
// auto gpuSensors = mGPUSensorsFacade->TemperatureSensors();
// tempSensors.insert(tempSensors.end(), gpuSensors.begin(),
// gpuSensors.end());
@@ -21,3 +31,5 @@ vector<shared_ptr<Sensor>> SensorManager::TemperatureSensors() {
vector<shared_ptr<Sensor>> SensorManager::RPMSensors() {
return mLMSensorsFacade->RPMSensors();
}
void SensorManager::ReloadSensors() { mLMSensorsFacade->ReloadSensors(); }