/** * 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, QTimer *newUpdater, QTimer *newNotifier, Notification *newNotification, QObject *parent) : QObject(parent) { QString filename; settings = newSettings; updateTimer = newUpdater; notifyTimer = newNotifier; notification = newNotification; // 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(); connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateData())); connect(settings, SIGNAL(configChanged()), this, SLOT(updateConfig())); connect(notifyTimer, SIGNAL(timeout()), this, SLOT(showNotification())); updateConfig(); updateTimer->setInterval(5000); updateTimer->start(); if(settings->getNotificationsEnabled()) { showNotification(); } } 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); } } } void Battery::updateConfig() { notifyTimer->stop(); notifyTimer->setInterval(settings->getInterval() * 1000); if(settings->getNotificationsEnabled()) notifyTimer->start(); } void Battery::showNotification() { if(!settings->getNotificationsEnabled()) return; qDebug() << "battery" << charge << "low" << settings->getLowAlert() << "high" << settings->getHighAlert() << "state" << state; if(charge <= settings->getLowAlert() && state.compare("charging")) { qInfo() << "Battery notification timer: empty enough battery"; notification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationLowText(), settings->getLowAlertFile()); } else if((charge >= settings->getHighAlert() && state.compare("discharging")) || (charge == 100 && !state.compare("idle"))) { qInfo() << "Battery notification timer: full enough battery"; notification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationHighText(), settings->getHighAlertFile()); } else { qInfo() << "Battery notification timer: close notification"; notification->close(); } } 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() { qDebug() << "\nPreparing for exit..."; blockSignals(true); if(updateTimer) { updateTimer->stop(); qDebug() << "Timer stopped"; } setChargingEnabled(true); chargingEnabledFile->setPermissions(originalPerms); qDebug() << "Charger control file permissions restored."; }