diff --git a/service/service.pro b/service/service.pro
index ebb87f9..e7acf4c 100644
--- a/service/service.pro
+++ b/service/service.pro
@@ -13,10 +13,12 @@ DEFINES += APP_VERSION=\"\\\"$$VERSION\\\"\"
DEFINES += APP_NAME=\"\\\"$$TARGET\\\"\"
HEADERS += \
- src/filewatcher.h
+ src/battery.h \
+ src/settings.h
SOURCES += \
- src/filewatcher.cpp \
+ src/battery.cpp \
+ src/settings.cpp \
src/harbour-batterybuddy-daemon.cpp
OTHER_FILES += harbour-batterybuddy-daemon.service
diff --git a/service/src/battery.cpp b/service/src/battery.cpp
new file mode 100644
index 0000000..5d75883
--- /dev/null
+++ b/service/src/battery.cpp
@@ -0,0 +1,181 @@
+/**
+ * Battery Buddy, a Sailfish application to prolong battery lifetime
+ *
+ * Copyright (C) 2019-2020 Matti Viljanen
+ *
+ * Battery Buddy is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Battery Buddy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details. You should have received a copy of the GNU
+ * General Public License along with Battery Buddy. If not, see .
+ *
+ * Author: Matti Viljanen
+ */
+#include "battery.h"
+
+Battery::Battery(Settings* newSettings, QObject* parent) : QObject(parent)
+{
+ QString filename;
+ settings = newSettings;
+
+ // Number: charge percentage, e.g. 42
+ chargeFile = new QFile("/sys/class/power_supply/battery/capacity", this);
+ qInfo() << "Reading capacity from" << chargeFile->fileName();
+
+ // String: charging, discharging, full, empty, unknown (others?)
+ stateFile = new QFile("/sys/class/power_supply/battery/status", this);
+ qInfo() << "Reading charge state from" << stateFile->fileName();
+
+ // Number: 0 or 1
+ chargerConnectedFile = new QFile("/sys/class/power_supply/usb/present", this);
+ qInfo() << "Reading charger status from" << chargerConnectedFile->fileName();
+
+ // ENABLE/DISABLE CHARGING
+ if(QHostInfo::localHostName().contains("SailfishEmul")) {
+ qInfo() << "Sailfish SDK detected";
+ qInfo() << "Using dummy control file";
+ filename = QStandardPaths::writableLocation(QStandardPaths::TempLocation)+"/charging_enabled_dummy";
+ chargingEnabledFile = new QFile(filename, this);
+ enableChargingValue = 1;
+ disableChargingValue = 0;
+ }
+ else {
+ // e.g. for Sony Xperia XA2
+ filename = "/sys/class/power_supply/battery/input_suspend";
+ if(!chargingEnabledFile && QFile::exists(filename)) {
+ chargingEnabledFile = new QFile(filename, this);
+ enableChargingValue = 0;
+ disableChargingValue = 1;
+ }
+
+ // e.g. for Sony Xperia Z3 Compact Tablet
+ filename = "/sys/class/power_supply/battery/charging_enabled";
+ if(!chargingEnabledFile && QFile::exists(filename)) {
+ chargingEnabledFile = new QFile(filename, this);
+ enableChargingValue = 1;
+ disableChargingValue = 0;
+ }
+
+ // e.g. for Jolla Phone
+ filename = "/sys/class/power_supply/usb/charger_disable";
+ if(!chargingEnabledFile && QFile::exists(filename)) {
+ chargingEnabledFile = new QFile(filename, this);
+ enableChargingValue = 0;
+ disableChargingValue = 1;
+ }
+
+
+ if(!chargingEnabledFile) {
+ qWarning() << "Charger control file not found!";
+ qWarning() << "Please contact the developer with your device model!";
+ }
+ }
+
+ // If we found a usable file, check that it is writable
+ if(chargingEnabledFile) {
+ // This should always succeed, since the service is started as root
+ if(chargingEnabledFile->open(QIODevice::WriteOnly)) {
+ qInfo() << "Controlling charging via" << chargingEnabledFile->fileName();
+ chargingEnabledFile->close();
+
+ originalPerms = chargingEnabledFile->permissions();
+
+ if(originalPerms | customPerms) {
+ chargingEnabledFile->setPermissions(customPerms);
+ qDebug() << "Charger control file permissions updated.";
+ }
+ }
+ else {
+ delete chargingEnabledFile;
+ chargingEnabledFile = Q_NULLPTR;
+ qWarning() << "Charger control file" << chargingEnabledFile->fileName() << "is not writable";
+ qWarning() << "Charger control feature disabled";
+ }
+ }
+
+ // TODO
+ // Implement DBus mechanism for reading battery status, or try
+ // QFileSystemWatcher again without /run/state/namespaces/Battery/
+ // thingamabob - it is deprecated anyway.
+
+ updateData();
+}
+
+Battery::~Battery() { }
+
+void Battery::updateData()
+{
+ if(chargeFile->open(QIODevice::ReadOnly)) {
+ nextCharge = chargeFile->readLine().trimmed().toInt();
+ if(nextCharge != charge) {
+ charge = nextCharge;
+ emit chargeChanged(charge);
+ qDebug() << "Battery:" << charge;
+ }
+ chargeFile->close();
+ }
+ if(chargerConnectedFile->open(QIODevice::ReadOnly)) {
+ nextChargerConnected = chargerConnectedFile->readLine().trimmed().toInt();
+ if(nextChargerConnected != chargerConnected) {
+ chargerConnected = nextChargerConnected;
+ emit chargerConnectedChanged(chargerConnected);
+ qDebug() << "Charger is connected:" << chargerConnected;
+ }
+ chargerConnectedFile->close();
+ }
+ if(stateFile->open(QIODevice::ReadOnly)) {
+ nextState = (QString(stateFile->readLine().trimmed().toLower()));
+ if(nextState != state) {
+ state = nextState;
+ emit stateChanged(state);
+ qDebug() << "Charging status:" << state;
+ }
+ stateFile->close();
+ }
+ if(chargingEnabledFile && settings->getLimitEnabled()) {
+ if(chargingEnabled && charge >= settings->getHighLimit()) {
+ qDebug() << "Disabling";
+ setChargingEnabled(false);
+ }
+ else if(!chargingEnabled && charge <= settings->getLowLimit()) {
+ qDebug() << "Enabling";
+ setChargingEnabled(true);
+ }
+ }
+}
+
+int Battery::getCharge(){ return charge; }
+
+QString Battery::getState() { return state; }
+
+bool Battery::getChargingEnabled() { return chargingEnabled; }
+
+void Battery::setChargingEnabled(bool isEnabled) {
+ if(chargingEnabledFile && chargingEnabledFile->open(QIODevice::WriteOnly)) {
+ if(chargingEnabledFile->write(QString("%1").arg(isEnabled ? enableChargingValue : disableChargingValue).toLatin1())) {
+ chargingEnabled = isEnabled;
+ emit chargingEnabledChanged(chargingEnabled);
+
+ if(isEnabled) {
+ qInfo() << "Charging resumed";
+ }
+ else {
+ qInfo() << "Charging paused";
+ }
+ }
+ chargingEnabledFile->close();
+ }
+}
+
+bool Battery::getChargerConnected() {
+ return chargerConnected;
+}
+
+void Battery::shutdown() {
+ chargingEnabledFile->setPermissions(originalPerms);
+ qDebug() << "Charger control file permissions updated.";
+}
diff --git a/service/src/battery.h b/service/src/battery.h
new file mode 100644
index 0000000..bf35d52
--- /dev/null
+++ b/service/src/battery.h
@@ -0,0 +1,85 @@
+/**
+ * Battery Buddy, a Sailfish application to prolong battery lifetime
+ *
+ * Copyright (C) 2019 Matti Viljanen
+ *
+ * Battery Buddy is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Battery Buddy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details. You should have received a copy of the GNU
+ * General Public License along with Battery Buddy. If not, see .
+ *
+ * Author: Matti Viljanen
+ */
+#ifndef BATTERY_H
+#define BATTERY_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "settings.h"
+
+class Battery : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int charge READ getCharge NOTIFY chargeChanged)
+ Q_PROPERTY(bool chargerConnected READ getChargerConnected NOTIFY chargerConnectedChanged)
+ Q_PROPERTY(QString state READ getState NOTIFY stateChanged)
+ Q_PROPERTY(bool chargingEnabled READ getChargingEnabled WRITE setChargingEnabled NOTIFY chargingEnabledChanged)
+
+public:
+ Battery(Settings* newSettings, QObject* parent = nullptr);
+ ~Battery();
+
+ int getCharge();
+ bool getCharging();
+ bool getChargerConnected();
+ QString getState();
+
+ bool getChargingEnabled();
+ void setChargingEnabled(bool);
+
+public slots:
+ void updateData();
+ void shutdown();
+
+private:
+ QFile* chargeFile;
+ QFile* chargerConnectedFile;
+ QFile* stateFile;
+ QFile* chargingEnabledFile = Q_NULLPTR;
+ Settings* settings;
+
+ // Default values:
+ int charge = 100; // 100% full
+ bool chargerConnected = false; // Charger plugged in
+ QString state = "idle"; // dis/charging, idle, unknown
+ bool chargingEnabled = true; // Only ever disabled manually
+
+ int enableChargingValue = 1;
+ int disableChargingValue = 0;
+ bool chargerIsEnabled = true;
+
+ int nextCharge = charge;
+ bool nextChargerConnected = chargerConnected;
+ QString nextState = state;
+ bool nextChargingEnabled = chargingEnabled;
+
+ QFileDevice::Permissions originalPerms; // Updated in constructor
+ QFileDevice::Permissions customPerms = static_cast(0x0666);
+
+signals:
+ void chargeChanged(int);
+ void stateChanged(QString);
+ void chargingEnabledChanged(bool);
+ void chargerConnectedChanged(bool);
+};
+
+#endif // BATTERY_H
diff --git a/service/src/filewatcher.cpp b/service/src/filewatcher.cpp
deleted file mode 100644
index c654ec5..0000000
--- a/service/src/filewatcher.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#include "filewatcher.h"
-
-FileWatcher::FileWatcher(QObject *parent) : QObject(parent)
-{
- QString filename;
-
- // Number: charge percentage, e.g. 42
- chargeFile = new QFile("/sys/class/power_supply/battery/capacity", this);
- qInfo() << "Reading capacity from" << chargeFile->fileName();
-
- // String: charging, discharging, full, empty, unknown (others?)
- stateFile = new QFile("/sys/class/power_supply/battery/status", this);
- qInfo() << "Reading charge state from" << stateFile->fileName();
-
- // Number: 0 or 1
- chargerConnectedFile = new QFile("/sys/class/power_supply/usb/present", this);
- qInfo() << "Reading charger status from" << chargerConnectedFile->fileName();
-
- // Find and use the file to control the charger
-
- // e.g. for Sony Xperia XA2
- filename = "/sys/class/power_supply/battery/input_suspend";
- if(!chargerControlFile && QFile::exists(filename)) {
- chargerControlFile = new QFile(filename, this);
- enableChargingValue = 0;
- disableChargingValue = 1;
- }
-
- // e.g. for Sony Xperia Z3 Compact Tablet
- filename = "/sys/class/power_supply/battery/charging_enabled";
- if(!chargerControlFile && QFile::exists(filename)) {
- chargerControlFile = new QFile(filename, this);
- enableChargingValue = 1;
- disableChargingValue = 0;
- }
-
- // e.g. for Jolla Phone
- filename = "/sys/class/power_supply/usb/charger_disable";
- if(!chargerControlFile && QFile::exists(filename)) {
- chargerControlFile = new QFile(filename, this);
- enableChargingValue = 0;
- disableChargingValue = 1;
- }
-
-
- if(!chargerControlFile) {
- qWarning() << "Charger control file not found!";
- qWarning() << "Please contact the developer with your device model!";
- }
-
- // If we found a usable file, check that it is writable
- if(chargerControlFile) {
- if(chargerControlFile->open(QIODevice::WriteOnly)) {
- qInfo() << "Controlling charging via" << chargerControlFile->fileName();
- chargerControlFile->close();
- }
- else {
- delete chargerControlFile;
- chargerControlFile = Q_NULLPTR;
- qWarning() << "Charger control file" << chargerControlFile->fileName() << "is not writable";
- qWarning() << "Charger control feature disabled";
- }
- }
-
- chargeWatcher = new QFileSystemWatcher(QStringList(chargeFile->fileName()), this);
- stateWatcher = new QFileSystemWatcher(QStringList(stateFile->fileName()), this);
- chargerConnectedWatcher = new QFileSystemWatcher(QStringList(chargerConnectedFile->fileName()), this);
-
- connect(chargeWatcher, SIGNAL(fileChanged(QString)), this, SLOT(updateCharge()));
- connect(stateWatcher, SIGNAL(fileChanged(QString)), this, SLOT(updateState()));
- connect(chargerConnectedWatcher, SIGNAL(fileChanged(QString)), this, SLOT(updateChargerConnected()));
-
- return;
-}
-
-FileWatcher::~FileWatcher()
-{
-
-}
-
-void FileWatcher::updateCharge()
-{
-
-}
-
-void FileWatcher::updateState()
-{
-
-}
-
-void FileWatcher::updateChargerConnected()
-{
-
-}
diff --git a/service/src/filewatcher.h b/service/src/filewatcher.h
deleted file mode 100644
index 729f5cf..0000000
--- a/service/src/filewatcher.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef FILEWATCHER_H
-#define FILEWATCHER_H
-
-#include
-#include
-#include
-
-#include
-
-class FileWatcher : public QObject
-{
- Q_OBJECT
-public:
- FileWatcher(QObject* parent = nullptr);
- ~FileWatcher();
-
-private:
- QFileSystemWatcher *chargeWatcher, *stateWatcher, *chargerConnectedWatcher;
- QFile *chargeFile, *stateFile, *chargerConnectedFile, *chargerControlFile;
- int enableChargingValue = 1, disableChargingValue = 0;
-
-private slots:
- void updateCharge();
- void updateState();
- void updateChargerConnected();
-};
-
-#endif // FILEWATCHER_H
diff --git a/service/src/harbour-batterybuddy-daemon.cpp b/service/src/harbour-batterybuddy-daemon.cpp
index 23c2b25..6703d21 100644
--- a/service/src/harbour-batterybuddy-daemon.cpp
+++ b/service/src/harbour-batterybuddy-daemon.cpp
@@ -1,6 +1,50 @@
-#include "filewatcher.h"
+#include
+#include
+#include
+#include "battery.h"
+#include "settings.h"
+#include
-int main()
+int main(int argc, char** argv)
{
- return 0;
+ // Use the same config file as the GUI application.
+ // This is used by QSettings()
+ qputenv("XDG_CONFIG_HOME", "/home/nemo/.config");
+
+ const char* logEnvVar = "QT_LOGGING_RULES";
+ for(int i = 1; i < argc; i++) {
+ if(!strcmp(argv[i],"-v")) {
+ printf("%s %s\n", APP_NAME, APP_VERSION);
+ return EXIT_SUCCESS;
+ }
+ else if(!strcmp(argv[i],"--verbose"))
+ qputenv(logEnvVar, "*.info=true;*.debug=false");
+ else if(!strcmp(argv[i],"--debug"))
+ qputenv(logEnvVar, "*.info=true");
+ else if(!strcmp(argv[i],"--help")) {
+ printf("%s %s\n\n", APP_NAME, APP_VERSION);
+ printf("This binary is meant to run as a service with root access,\n");
+ printf("but it can be run manually for debugging purposes, too.\n\n");
+ printf("Usage:\n");
+ printf(" --verbose Enable informational messages\n");
+ printf(" --debug Enable informational and debugging messages\n");
+ printf(" --help Print version string and exit\n");
+ return EXIT_SUCCESS;
+ }
+ }
+ if(!qEnvironmentVariableIsSet(logEnvVar))
+ qputenv(logEnvVar, "*.info=false;*.debug=false");
+
+ QCoreApplication app(argc, argv);
+
+ Settings* settings = new Settings();
+ Battery* battery = new Battery(settings);
+
+ QTimer* updater = new QTimer();
+ QObject::connect(updater, SIGNAL(timeout()), battery, SLOT(updateData()));
+ updater->start(3000);
+
+ int retval = app.exec();
+
+ return retval;
}
diff --git a/service/src/settings.cpp b/service/src/settings.cpp
new file mode 100644
index 0000000..0ed7975
--- /dev/null
+++ b/service/src/settings.cpp
@@ -0,0 +1,111 @@
+/**
+ * Battery Buddy, a Sailfish application to prolong battery lifetime
+ *
+ * Copyright (C) 2019-2020 Matti Viljanen
+ *
+ * Battery Buddy is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Battery Buddy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details. You should have received a copy of the GNU
+ * General Public License along with Battery Buddy. If not, see .
+ *
+ * Author: Matti Viljanen
+ */
+#include "settings.h"
+
+Settings::Settings(QObject *parent) : QObject(parent)
+{
+ // Use the same file location as GUI for data exchange
+ if(!mySettings) {
+ mySettings = new QSettings("harbour-batterybuddy", "harbour-batterybuddy");
+ }
+ qDebug() << "Using" << mySettings->fileName();
+
+ // Migrate old settings
+ if(mySettings->contains("lowerLimit")) {
+ mySettings->setValue(sLowAlert, mySettings->value("lowerLimit"));
+ mySettings->remove("lowerLimit");
+ qInfo() << "Migrated old lowerLimit value";
+ }
+
+ if(mySettings->contains("upperLimit")) {
+ mySettings->setValue(sHighAlert, mySettings->value("upperLimit"));
+ mySettings->remove("upperLimit");
+ qInfo() << "Migrated old upperLimit value";
+ }
+
+ // Do this here, because...
+ watcher = new QFileSystemWatcher(QStringList(mySettings->fileName()));
+ connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(configChanged(QString)));
+
+ // ...calling this deletes mySettings!
+ configChanged(mySettings->fileName());
+
+ qInfo() << "Loaded" << sLimitEnabled << limitEnabled;
+
+ // Battery Buddy GUI application changes the settings file,
+ // so we must monitor it and update when it changes.
+
+}
+
+Settings::~Settings()
+{
+ delete mySettings;
+}
+
+int Settings::bound(int value, int min, int max) {
+ return (value <= min ? min : (value >= max ? max : value));
+}
+
+void Settings::loadInteger(const char* key, int *value, int min, int max) {
+ *value = bound(mySettings->value(key, *value).toInt(), min, max);
+ qInfo() << "Loaded" << key << *value;
+}
+
+void Settings::configChanged(QString path) {
+
+ // Use the same file location as GUI for data exchange
+ if(!mySettings) {
+ mySettings = new QSettings("harbour-batterybuddy", "harbour-batterybuddy");
+ }
+
+ qDebug() << "Reading values...";
+ // Read in the values
+ loadInteger(sLowAlert, &lowAlert, 10, 99);
+ loadInteger(sHighAlert, &highAlert, 11, 100);
+ loadInteger(sInterval, &interval, 60, 600);
+ loadInteger(sLimitEnabled, &limitEnabled, 0, 1);
+ loadInteger(sNotificationsEnabled, ¬ificationsEnabled, 0, 1);
+ loadInteger(sLowLimit, &lowLimit, 20, 94);
+ loadInteger(sHighLimit, &highLimit, 21, 95);
+ qDebug() << "Values read.";
+
+ delete mySettings;
+ mySettings = nullptr;
+
+ // Let the file system settle...
+ QThread::msleep(50);
+
+ if(watcher->files().contains(path)) {
+ qDebug() << "File OK";
+ }
+ else {
+ qDebug() << "File replaced, re-adding.";
+ watcher->addPath(path);
+ }
+}
+
+// Getters condensed
+int Settings::getLowAlert() { return lowAlert; }
+int Settings::getHighAlert() { return highAlert; }
+int Settings::getInterval() { return interval; }
+int Settings::getLowLimit() { return lowLimit; }
+int Settings::getHighLimit() { return highLimit; }
+bool Settings::getLimitEnabled() { return limitEnabled == 1; }
+bool Settings::getNotificationsEnabled() { return notificationsEnabled == 1; }
+QString Settings::getLowAlertFile() { return lowAlertFile; }
+QString Settings::getHighAlertFile() { return highAlertFile; }
diff --git a/service/src/settings.h b/service/src/settings.h
new file mode 100644
index 0000000..2d13ffc
--- /dev/null
+++ b/service/src/settings.h
@@ -0,0 +1,82 @@
+/**
+ * Battery Buddy, a Sailfish application to prolong battery lifetime
+ *
+ * Copyright (C) 2019-2020 Matti Viljanen
+ *
+ * Battery Buddy is free software: you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Battery Buddy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details. You should have received a copy of the GNU
+ * General Public License along with Battery Buddy. If not, see .
+ *
+ * Author: Matti Viljanen
+ */
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include
+#include
+#include
+#include
+#include
+
+class Settings : public QObject
+{
+ Q_OBJECT
+
+public:
+ Settings(QObject* parent = nullptr);
+ ~Settings();
+
+ int getLowAlert();
+ int getHighAlert();
+ int getInterval();
+ int getLowLimit();
+ int getHighLimit();
+ bool getLimitEnabled();
+ bool getNotificationsEnabled();
+ QString getLowAlertFile();
+ QString getHighAlertFile();
+
+private:
+ QSettings* mySettings = nullptr;
+ QFileSystemWatcher *watcher = nullptr;
+
+ // Default values
+ int lowAlert = 25;
+ int highAlert = 75;
+ int interval = 60;
+
+ // Converted to boolean for QML
+ int limitEnabled = 0;
+ int notificationsEnabled = 1;
+ int daemonEnabled = 1;
+
+ int lowLimit = 65;
+ int highLimit = 70;
+ QString lowAlertFile = "/usr/share/sounds/jolla-ambient/stereo/general_warning.wav";
+ QString highAlertFile = "/usr/share/sounds/jolla-ambient/stereo/positive_confirmation.wav";
+
+ // To avoid repeating the same string over and over and over...
+ const char* sLowAlert = "lowAlert";
+ const char* sHighAlert = "highAlert";
+ const char* sInterval = "interval";
+ const char* sLimitEnabled = "limitEnabled";
+ const char* sNotificationsEnabled = "notificationsEnabled";
+ const char* sLowLimit = "lowLimit";
+ const char* sHighLimit = "highLimit";
+ const char* sLowAlertFile = "lowAlertFile";
+ const char* sHighAlertFile = "highAlertFile";
+
+ int bound(int value, int min, int max);
+ void loadInteger(const char *key, int *value, int min, int max);
+
+private slots:
+ void configChanged(QString path);
+};
+
+#endif // SETTINGS_H