diff --git a/application/qml/pages/MainPage.qml b/application/qml/pages/MainPage.qml index d6cf582..deff4be 100644 --- a/application/qml/pages/MainPage.qml +++ b/application/qml/pages/MainPage.qml @@ -31,6 +31,14 @@ Page { "empty": qsTr("empty", "Battery fully depleted"), "unknown": qsTr("unknown", "Battery not detected, or faulty, or something") } + property variant healthText: { + "good": qsTr("Good", "Battery is OK"), + "warm": qsTr("Warm", "Battery is warm"), + "overheat": qsTr("Overheated", "Battery is very hot"), + "cool": qsTr("Cool", "Battery is cool"), + "cold": qsTr("Cold", "Battery is very cold"), + "unknown": qsTr("unknown", "Battery not detected, or faulty, or something") + } property bool serviceRunning: true Timer { @@ -166,6 +174,23 @@ Page { label: qsTr("State:") value: statusText[battery.state] } + MyDetailItem { + label: qsTr("Health:") + value: healthText[battery.health] + visible: value !== "unknown" + } + MyDetailItem { + label: qsTr("Temperature:") + value: battery.temperature === 0x7FFFFFFF ? healthText["unknown"] : formatTemperature(battery.temperature) + visible: battery.temperature !== 0x7FFFFFFF + + function formatTemperature(temp) { + if(Qt.locale().measurementSystem === Locale.ImperialUSSystem) { + return Math.floor((battery.temperature / 10) * 1.8 + 32) + " °F" + } + return Math.floor(battery.temperature / 10) + " °C" + } + } } } Column { diff --git a/application/qml/pages/SettingsPage.qml b/application/qml/pages/SettingsPage.qml index 3c9b06c..a47fd00 100644 --- a/application/qml/pages/SettingsPage.qml +++ b/application/qml/pages/SettingsPage.qml @@ -43,9 +43,11 @@ Page { highLimitSlider.value = settings.highLimit lowLimitSlider.value = settings.lowLimit highAlertSlider.value = settings.highAlert + healthSelector.currentIndex = settings.healthAlert lowAlertSlider.value = settings.lowAlert highIntervalSlider.value = settings.highNotificationsInterval lowIntervalSlider.value = settings.lowNotificationsInterval + healthIntervalSlider.value = settings.healthNotificationsInterval if(logger.debug) logger.log("SettingsPage values updated") daemonCheck.start() } @@ -365,6 +367,70 @@ Page { smallChange: 10 largeChange: 60 } + + Label { + x: Theme.paddingLarge + text: qsTr("Health notification settings") + color: Theme.highlightColor + } + Label { + text: qsTr("Display visual and audible notifications when the battery status exceeds safe values.
This usually means high (or low) temperature but can include other parameters depending on the hardware.") + anchors { + left: parent.left + right: parent.right + leftMargin: Theme.horizontalPageMargin*2 + rightMargin: Theme.horizontalPageMargin + } + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeExtraSmall + wrapMode: Text.Wrap + } + + SectionHeader { text: qsTr("Battery health notification") } + + ComboBox { + id: healthSelector + width: parent.width + label: qsTr("Notification treshold") + currentIndex: settings.healthAlert + menu: ContextMenu { + MenuItem { text: qsTr("Never") } + MenuItem { text: qsTr("Warning") } + MenuItem { text: qsTr("Critical") } + } + onValueChanged: save() + function save() { + settings.healthAlert = healthSelector.currentIndex + } + } + + SectionHeader { text: qsTr("Health notification interval") } + + MySlider { + id: healthIntervalSlider + minimumValue: 50 + maximumValue: 610 + stepSize: 10 + valueText: updateValueText() + onValueChanged: updateValueText() + function updateValueText() { + if(value == 50) + return qsTr("Once") + if(value == 610) + return qsTr("Never") + return Math.floor(value / 60) + (value % 60 < 10 ? ":0" + value % 60 : ":" + value % 60) + } + onReleased: save() + function save() { + settings.healthNotificationsInterval = value + } + } + + AdjustmentButtons { + targetSlider: healthIntervalSlider + smallChange: 10 + largeChange: 60 + } } } } diff --git a/application/src/battery.cpp b/application/src/battery.cpp index 06baa98..9aa86ad 100644 --- a/application/src/battery.cpp +++ b/application/src/battery.cpp @@ -40,6 +40,18 @@ Battery::Battery(Settings* newSettings, Logger* newLogger, QObject* parent) : QO QString filename; + // Number: temperature + filename = "/sys/class/power_supply/battery/temp"; + if(!temperatureFile && QFile::exists(filename)) { + temperatureFile = new QFile(filename, this); + } + + // String: health state + filename = "/sys/class/power_supply/battery/health"; + if(!healthFile && QFile::exists(filename)) { + healthFile = new QFile(filename, this); + } + // e.g. for Sony Xperia XA2 filename = "/sys/class/power_supply/battery/input_suspend"; if(!chargingEnabledFile && QFile::exists(filename)) { diff --git a/application/src/battery.h b/application/src/battery.h index b059d86..526af7a 100644 --- a/application/src/battery.h +++ b/application/src/battery.h @@ -67,6 +67,10 @@ private: QString state = "idle"; // dis/charging, idle, unknown bool chargingEnabled = true; // Only ever disabled manually + + QString health = "unknown"; // Good, warm, overheat. Might have Cold or Overvoltage depending on driver + int temperature = 0x7FFFFFFF; // This value means "unknown" (32-bit INT_MAX) + int enableChargingValue = 1; int disableChargingValue = 0; bool chargerIsEnabled = true; diff --git a/application/src/settings.cpp b/application/src/settings.cpp index 04ef177..ebf9e42 100644 --- a/application/src/settings.cpp +++ b/application/src/settings.cpp @@ -31,8 +31,10 @@ Settings::Settings(Logger *newLogger, QObject *parent) : QObject(parent) // Read in the values loadInteger(sLowAlert, lowAlert, 5, 99); loadInteger(sHighAlert, highAlert, 6, 100); + loadInteger(sHealthAlert, healthAlert, 0, 2); loadInteger(sHighNotificationsInterval, highNotificationsInterval, 50, 610); loadInteger(sLowNotificationsInterval, lowNotificationsInterval, 50, 610); + loadInteger(sHealthNotificationsInterval, healthNotificationsInterval, 50, 610); loadInteger(sLimitEnabled, limitEnabled, 0, 1); loadInteger(sLowLimit, lowLimit, 5, 99); loadInteger(sHighLimit, highLimit, 6, 100); @@ -43,10 +45,16 @@ Settings::Settings(Logger *newLogger, QObject *parent) : QObject(parent) loadString(sNotificationTitle, notificationTitle); loadString(sNotificationLowText, notificationLowText); loadString(sNotificationHighText, notificationHighText); + loadString(sNotificationHealthTitle, notificationHealthTitle); + loadString(sNotificationHealthWarnText, notificationHealthWarnText); + loadString(sNotificationHealthCritText, notificationHealthCritText); saveString(sNotificationTitle, tr("Battery charge %1%"), notificationTitle); saveString(sNotificationLowText, tr("Please connect the charger."), notificationLowText); saveString(sNotificationHighText, tr("Please disconnect the charger."), notificationHighText); + saveString(sNotificationHealthTitle, tr("Battery health %1"), notificationHealthTitle); + saveString(sNotificationHealthWarnText, tr("Battery health is not good"), notificationHealthWarnText); + saveString(sNotificationHealthCritText, tr("Battery health is critical"), notificationHealthCritText); } Settings::~Settings() @@ -56,20 +64,26 @@ Settings::~Settings() } // Getters condensed. -int Settings::getLowAlert() { return lowAlert; } -int Settings::getHighAlert() { return highAlert; } -int Settings::getHighNotificationsInterval() { return highNotificationsInterval; } -int Settings::getLowNotificationsInterval() { return lowNotificationsInterval; } -int Settings::getLowLimit() { return lowLimit; } -int Settings::getHighLimit() { return highLimit; } -bool Settings::getLimitEnabled() { return limitEnabled == 1; } -QString Settings::getLowAlertFile() { return lowAlertFile; } -QString Settings::getHighAlertFile() { return highAlertFile; } -QString Settings::getLogFilename() { return logFilename; } -QString Settings::getNotificationTitle() { return notificationTitle; } -QString Settings::getNotificationLowText() { return notificationLowText; } -QString Settings::getNotificationHighText() { return notificationHighText; } -int Settings::getLogLevel() { return logLevel; } +int Settings::getLowAlert() { return lowAlert; } +int Settings::getHighAlert() { return highAlert; } +int Settings::getHealthAlert() { return healthAlert; } +int Settings::getHighNotificationsInterval() { return highNotificationsInterval; } +int Settings::getLowNotificationsInterval() { return lowNotificationsInterval; } +int Settings::getHealthNotificationsInterval() { return healthNotificationsInterval; } +int Settings::getLowLimit() { return lowLimit; } +int Settings::getHighLimit() { return highLimit; } +bool Settings::getLimitEnabled() { return limitEnabled == 1; } +QString Settings::getLowAlertFile() { return lowAlertFile; } +QString Settings::getHighAlertFile() { return highAlertFile; } +QString Settings::getHealthAlertFile() { return healthAlertFile; } +QString Settings::getLogFilename() { return logFilename; } +QString Settings::getNotificationTitle() { return notificationTitle; } +QString Settings::getNotificationLowText() { return notificationLowText; } +QString Settings::getNotificationHighText() { return notificationHighText; } +QString Settings::getNotificationHealthTitle() { return notificationHealthTitle; } +QString Settings::getNotificationHealthWarnText() { return notificationHealthWarnText; } +QString Settings::getNotificationHealthCritText() { return notificationHealthCritText; } +int Settings::getLogLevel() { return logLevel; } void Settings::setLowAlert(const int newLimit) { if(saveInteger(sLowAlert, newLimit, lowAlert)) { @@ -83,6 +97,12 @@ void Settings::setHighAlert(const int newLimit) { } } +void Settings::setHealthAlert(const int newLimit) { + if(saveInteger(sHealthAlert, newLimit, healthAlert)) { + emit healthAlertChanged(healthAlert); + } +} + void Settings::setHighNotificationsInterval(const int newInterval) { if(saveInteger(sHighNotificationsInterval, newInterval, highNotificationsInterval)) { emit highNotificationsIntervalChanged(highNotificationsInterval); @@ -95,6 +115,12 @@ void Settings::setLowNotificationsInterval(const int newInterval) { } } +void Settings::setHealthNotificationsInterval(const int newInterval) { + if(saveInteger(sHealthNotificationsInterval, newInterval, healthNotificationsInterval)) { + emit healthNotificationsIntervalChanged(healthNotificationsInterval); + } +} + void Settings::setLowLimit(const int newLimit) { if(saveInteger(sLowLimit, newLimit, lowLimit)) { emit lowLimitChanged(lowLimit); @@ -126,6 +152,21 @@ void Settings::setNotificationHighText(const QString newText) { emit notificationHighTextChanged(notificationHighText); } +void Settings::setNotificationHealthTitle(const QString newText) { + if(saveString(sNotificationHealthTitle, newText, notificationTitle)) + emit notificationHealthTitleChanged(notificationTitle); +} + +void Settings::setNotificationHealthWarnText(const QString newText) { + if(saveString(sNotificationHealthWarnText, newText, notificationHealthWarnText)) + emit notificationHealthWarnTextChanged(notificationHealthWarnText); +} + +void Settings::setNotificationHealthCritText(const QString newText) { + if(saveString(sNotificationHealthCritText, newText, notificationHealthCritText)) + emit notificationHealthCritTextChanged(notificationHealthCritText); +} + void Settings::setLogLevel(const int newLogLevel) { if(saveInteger(sLogLevel, newLogLevel, logLevel)) emit logLevelChanged(logLevel); diff --git a/application/src/settings.h b/application/src/settings.h index eecc6a0..b30fc7d 100644 --- a/application/src/settings.h +++ b/application/src/settings.h @@ -27,16 +27,22 @@ class Settings : public QObject Q_OBJECT Q_PROPERTY(int highAlert READ getHighAlert WRITE setHighAlert NOTIFY highAlertChanged) Q_PROPERTY(int lowAlert READ getLowAlert WRITE setLowAlert NOTIFY lowAlertChanged) + Q_PROPERTY(int healthAlert READ getHealthAlert WRITE setHealthAlert NOTIFY healthAlertChanged) Q_PROPERTY(int highNotificationsInterval READ getHighNotificationsInterval WRITE setHighNotificationsInterval NOTIFY highNotificationsIntervalChanged) Q_PROPERTY(int lowNotificationsInterval READ getLowNotificationsInterval WRITE setLowNotificationsInterval NOTIFY lowNotificationsIntervalChanged) + Q_PROPERTY(int healthNotificationsInterval READ getHealthNotificationsInterval WRITE setHealthNotificationsInterval NOTIFY healthNotificationsIntervalChanged) Q_PROPERTY(int highLimit READ getHighLimit WRITE setHighLimit NOTIFY highLimitChanged) Q_PROPERTY(int lowLimit READ getLowLimit WRITE setLowLimit NOTIFY lowLimitChanged) Q_PROPERTY(bool limitEnabled READ getLimitEnabled WRITE setLimitEnabled NOTIFY limitEnabledChanged) Q_PROPERTY(QString highAlertFile READ getHighAlertFile NOTIFY highAlertFileChanged) Q_PROPERTY(QString lowAlertFile READ getLowAlertFile NOTIFY lowAlertFileChanged) + Q_PROPERTY(QString healthAlertFile READ getHealthAlertFile NOTIFY healthAlertFileChanged) Q_PROPERTY(QString notificationTitle READ getNotificationTitle WRITE setNotificationTitle NOTIFY notificationTitleChanged) Q_PROPERTY(QString notificationLowText READ getNotificationLowText WRITE setNotificationLowText NOTIFY notificationLowTextChanged) Q_PROPERTY(QString notificationHighText READ getNotificationHighText WRITE setNotificationHighText NOTIFY notificationHighTextChanged) + Q_PROPERTY(QString notificationHealthTitle READ getNotificationHealthTitle WRITE setNotificationHealthTitle NOTIFY notificationHealthTitleChanged) + Q_PROPERTY(QString notificationHealthWarnText READ getNotificationHealthWarnText WRITE setNotificationHealthWarnText NOTIFY notificationHealthWarnTextChanged) + Q_PROPERTY(QString notificationHealthCritText READ getNotificationHealthCritText WRITE setNotificationHealthCritText NOTIFY notificationHealthCritTextChanged) Q_PROPERTY(QString logFilename READ getLogFilename NOTIFY logFilenameChanged) Q_PROPERTY(int logLevel READ getLogLevel WRITE setLogLevel NOTIFY logLevelChanged) @@ -46,29 +52,40 @@ public: int getLowAlert(); int getHighAlert(); + int getHealthAlert(); int getHighNotificationsInterval(); int getLowNotificationsInterval(); + int getHealthNotificationsInterval(); int getLowLimit(); int getHighLimit(); bool getLimitEnabled(); QString getLowAlertFile(); QString getHighAlertFile(); + QString getHealthAlertFile(); QString getNotificationTitle(); QString getNotificationLowText(); QString getNotificationHighText(); + QString getNotificationHealthTitle(); + QString getNotificationHealthWarnText(); + QString getNotificationHealthCritText(); QString getLogFilename(); int getLogLevel(); void setLowAlert(const int newLimit); void setHighAlert(const int newLimit); + void setHealthAlert(const int newLimit); void setHighNotificationsInterval(const int newInterval); void setLowNotificationsInterval(const int newInterval); + void setHealthNotificationsInterval(const int newInterval); void setLowLimit(const int newLimit); void setHighLimit(const int newLimit); void setLimitEnabled(const bool newEnabled); void setNotificationTitle(const QString newText); void setNotificationLowText(const QString newText); void setNotificationHighText(const QString newText); + void setNotificationHealthTitle(const QString newText); + void setNotificationHealthWarnText(const QString newText); + void setNotificationHealthCritText(const QString newText); void setLogLevel(const int newLogLevel); private: @@ -79,32 +96,44 @@ private: // Default values int lowAlert = 25; int highAlert = 75; + int healthAlert = 1; // warn int highNotificationsInterval = 60; int lowNotificationsInterval = 60; + int healthNotificationsInterval = 60; int limitEnabled = 1; // Converted to boolean for QML 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"; + QString healthAlertFile = lowAlertFile; QString notificationTitle; QString notificationLowText; QString notificationHighText; + QString notificationHealthTitle; + QString notificationHealthWarnText; + QString notificationHealthCritText; QString logFilename; int logLevel; // To avoid repeating the same string over and over and over... const char* sLowAlert = "lowAlert"; const char* sHighAlert = "highAlert"; + const char* sHealthAlert = "healthAlert"; const char* sHighNotificationsInterval = "highNotificationsInterval"; const char* sLowNotificationsInterval = "lowNotificationsInterval"; + const char* sHealthNotificationsInterval = "healthNotificationsInterval"; const char* sLimitEnabled = "limitEnabled"; const char* sLowLimit = "lowLimit"; const char* sHighLimit = "highLimit"; const char* sLowAlertFile = "lowAlertFile"; const char* sHighAlertFile = "highAlertFile"; + const char* sHealthAlertFile = "healthAlertFile"; const char* sNotificationTitle = "notificationTitle"; const char* sNotificationLowText = "notificationLowText"; const char* sNotificationHighText = "notificationHighText"; + const char* sNotificationHealthTitle = "notificationHealthTitle"; + const char* sNotificationHealthWarnText = "notificationHealthWarnText"; + const char* sNotificationHealthCritText = "notificationHealthCritText"; const char* sLogFilename = "logFilename"; const char* sLogLevel = "logLevel"; @@ -117,16 +146,22 @@ private: signals: void lowAlertChanged(int); void highAlertChanged(int); + void healthAlertChanged(int); void highNotificationsIntervalChanged(int); void lowNotificationsIntervalChanged(int); + void healthNotificationsIntervalChanged(int); void limitEnabledChanged(bool); void lowLimitChanged(int); void highLimitChanged(int); void lowAlertFileChanged(QString); void highAlertFileChanged(QString); + void healthAlertFileChanged(QString); void notificationTitleChanged(QString); void notificationLowTextChanged(QString); void notificationHighTextChanged(QString); + void notificationHealthTitleChanged(QString); + void notificationHealthWarnTextChanged(QString); + void notificationHealthCritTextChanged(QString); void logFilenameChanged(QString); void logLevelChanged(int); }; diff --git a/application/translations/harbour-batterybuddy-de_DE.ts b/application/translations/harbour-batterybuddy-de_DE.ts index a0bf919..df7968d 100644 --- a/application/translations/harbour-batterybuddy-de_DE.ts +++ b/application/translations/harbour-batterybuddy-de_DE.ts @@ -156,34 +156,34 @@ LogPage View log - + Logdatei ansehen Update - + Aktualisieren Copy - + Kopieren Log level - + Log Level Quiet Low log setting - + Still Verbose Medium log setting - + Mittel Debug High log setting - + Debug @@ -275,6 +275,39 @@ Current: Strom: + + Good + Battery is OK + die Batterie ist OK + + + Warm + Battery is warm + die Batterie ist warm + + + Overheated + Battery is very hot + die Batterie ist sehr heiss + + + Health: + Zustand: + + + Temperature: + Temperatur: + + + Cool + Battery is cool + der Batterie ist kalt + + + Cold + Battery is very cold + der Batterie ist sehr kalt + Settings @@ -290,6 +323,18 @@ Please disconnect the charger. Bitte Ladegerät trennen. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,6 +409,34 @@ View log + Logdatei ansehen + + + Battery health notification + Zustandsbenachrichtigung + + + Warning + Warnung + + + Critical + Kritisch + + + Health notification interval + Zustandsbenachrichtigungsintervall + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + Visuelle und akustische Benachrichtigungen anzeigen, wenn gewissen Schwellwerte erreicht werden. Meistens gilt das fuer die Temperatur, aber je nach Hardware koennen auch andere Faktoren zu einer Meldung fuehren. + + + Health notification settings + + + + Notification treshold diff --git a/application/translations/harbour-batterybuddy-fi.ts b/application/translations/harbour-batterybuddy-fi.ts index 2a36e3a..fdcdae0 100644 --- a/application/translations/harbour-batterybuddy-fi.ts +++ b/application/translations/harbour-batterybuddy-fi.ts @@ -273,6 +273,39 @@ Current: Virta: + + Good + Battery is OK + Hyvä + + + Warm + Battery is warm + Lämmin + + + Overheated + Battery is very hot + Ylikuumentunut + + + Cool + Battery is cool + Viileä + + + Cold + Battery is very cold + Kylmä + + + Health: + Kunto: + + + Temperature: + Lämpötila: + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. Ole hyvä ja irrota laturi. + + Battery health %1 + Akun kunto: %1 + + + Battery health is not good + Akun tila ei ole normaali + + + Battery health is critical + Akun tila on kriittinen + SettingsPage @@ -364,5 +409,33 @@ View log Näytä loki + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + Näytä ilmoitukset ja toista hälytysääni, kun akun kunto ylittää normaalit rahat.<br />Yleensä tämä tarkoittaa liian korkeaa (tai matalaa) lämpötilaa, mutta syitä voi olla muitakin, laitteistosta riippuen. + + + Warning + Varoitus + + + Critical + Kriittinen + + + Health notification interval + Akun kuntoilmoitusten aikaväli + + + Battery health notification + Akun kunnon ilmoitukset + + + Health notification settings + Akun tilan ilmoitusten aikaväli + + + Notification treshold + Ilmoitusten taso + diff --git a/application/translations/harbour-batterybuddy-fr.ts b/application/translations/harbour-batterybuddy-fr.ts index 57bf644..d1056c1 100644 --- a/application/translations/harbour-batterybuddy-fr.ts +++ b/application/translations/harbour-batterybuddy-fr.ts @@ -273,6 +273,39 @@ Current: Courant: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. Merci de débrancher le chargeur. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,5 +409,33 @@ View log + + Health notification settings + + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Battery health notification + + + + Notification treshold + + + + Warning + + + + Critical + + + + Health notification interval + + diff --git a/application/translations/harbour-batterybuddy-hu.ts b/application/translations/harbour-batterybuddy-hu.ts index 037d5d8..386decc 100644 --- a/application/translations/harbour-batterybuddy-hu.ts +++ b/application/translations/harbour-batterybuddy-hu.ts @@ -273,6 +273,39 @@ Current: Jelenleg: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. Kérlek húzd ki a töltőt. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,5 +409,33 @@ View log Napló megtekintése + + Health notification settings + + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Battery health notification + + + + Notification treshold + + + + Warning + + + + Critical + + + + Health notification interval + + diff --git a/application/translations/harbour-batterybuddy-pl.ts b/application/translations/harbour-batterybuddy-pl.ts index 78b9273..6e0af67 100644 --- a/application/translations/harbour-batterybuddy-pl.ts +++ b/application/translations/harbour-batterybuddy-pl.ts @@ -273,6 +273,39 @@ Current: Prąd: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. Odłącz ładowarkę. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,5 +409,33 @@ View log Pokaż logi + + Health notification settings + + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Battery health notification + + + + Notification treshold + + + + Warning + + + + Critical + + + + Health notification interval + + diff --git a/application/translations/harbour-batterybuddy-sv.ts b/application/translations/harbour-batterybuddy-sv.ts index eefd74b..78c0024 100644 --- a/application/translations/harbour-batterybuddy-sv.ts +++ b/application/translations/harbour-batterybuddy-sv.ts @@ -273,6 +273,39 @@ Current: Ström: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. Koppla ifrån laddaren. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,5 +409,33 @@ View log Visa logg + + Health notification settings + + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Battery health notification + + + + Notification treshold + + + + Warning + + + + Critical + + + + Health notification interval + + diff --git a/application/translations/harbour-batterybuddy-zh_CN.ts b/application/translations/harbour-batterybuddy-zh_CN.ts index 40e5856..1e42a0e 100644 --- a/application/translations/harbour-batterybuddy-zh_CN.ts +++ b/application/translations/harbour-batterybuddy-zh_CN.ts @@ -275,6 +275,39 @@ Current: 当前状态: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -290,6 +323,18 @@ Please disconnect the charger. 请断开充电器 + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -366,5 +411,33 @@ View log 查看日志 + + Health notification settings + + + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Battery health notification + + + + Notification treshold + + + + Warning + + + + Critical + + + + Health notification interval + + diff --git a/application/translations/harbour-batterybuddy.ts b/application/translations/harbour-batterybuddy.ts index e39464c..11aa9f6 100644 --- a/application/translations/harbour-batterybuddy.ts +++ b/application/translations/harbour-batterybuddy.ts @@ -273,6 +273,39 @@ Current: + + Good + Battery is OK + + + + Warm + Battery is warm + + + + Overheated + Battery is very hot + + + + Cool + Battery is cool + + + + Cold + Battery is very cold + + + + Health: + + + + Temperature: + + Settings @@ -288,6 +321,18 @@ Please disconnect the charger. + + Battery health %1 + + + + Battery health is not good + + + + Battery health is critical + + SettingsPage @@ -364,5 +409,33 @@ View log + + Display visual and audible notifications when the battery status exceeds safe values.<br />This usually means high (or low) temperature but can include other parameters depending on the hardware. + + + + Warning + + + + Critical + + + + Health notification interval + + + + Battery health notification + + + + Health notification settings + + + + Notification treshold + + diff --git a/service/src/battery.cpp b/service/src/battery.cpp index 5078293..88799f6 100644 --- a/service/src/battery.cpp +++ b/service/src/battery.cpp @@ -33,23 +33,38 @@ Battery::Battery(Logger* newLogger, bool loglevelSet, QObject *parent) : QObject updateTimer = new QTimer(this); highNotifyTimer = new QTimer(this); lowNotifyTimer = new QTimer(this); - notification = new MyNotification(this); + healthNotifyTimer = new QTimer(this); + chargeNotification = new MyNotification(this); + healthNotification = new MyNotification(this); // Number: charge percentage, e.g. 42 chargeFile = new QFile("/sys/class/power_supply/battery/capacity", this); - logE("Capacity file: " + chargeFile->fileName()); + logE("Capacity file: " + chargeFile->fileName() + (chargeFile->exists() ? " OK" : " doesn't exist")); // String: charging, discharging, full, empty, unknown (others?) stateFile = new QFile("/sys/class/power_supply/battery/status", this); - logE("Charge state file: " + stateFile->fileName()); + logE("Charge state file: " + stateFile->fileName() + (stateFile->exists() ? " OK" : " doesn't exist")); // Number: 0 or 1 chargerConnectedFile = new QFile("/sys/class/power_supply/usb/present", this); - logE("Charger status file: " + chargerConnectedFile->fileName()); + logE("Charger status file: " + chargerConnectedFile->fileName() + (chargerConnectedFile->exists() ? " OK" : " doesn't exist")); - // ENABLE/DISABLE CHARGING QString filename; + // Number: temperature + filename = "/sys/class/power_supply/battery/temp"; + if(!temperatureFile && QFile::exists(filename)) { + temperatureFile = new QFile(filename, this); + } + logE("Temperature file: " + filename + (QFile::exists(filename) ? " OK" : " doesn't exist")); + + // String: health state + filename = "/sys/class/power_supply/battery/health"; + if(!healthFile && QFile::exists(filename)) { + healthFile = new QFile(filename, this); + } + logE("Battery health file: " + filename + (QFile::exists(filename) ? " OK" : " doesn't exist")); + // e.g. for Sony Xperia XA2 filename = "/sys/class/power_supply/battery/input_suspend"; if(!chargingEnabledFile && QFile::exists(filename)) { @@ -76,7 +91,7 @@ Battery::Battery(Logger* newLogger, bool loglevelSet, QObject *parent) : QObject // If we found a usable file, check that it is writable if(chargingEnabledFile) { - logE("Charger control file: " + chargingEnabledFile->fileName()); + logE("Charger control file: " + chargingEnabledFile->fileName() + (chargingEnabledFile->exists() ? " OK" : " doesn't exist")); if(chargingEnabledFile->open(QIODevice::WriteOnly)) { chargingEnabledFile->close(); } @@ -97,6 +112,7 @@ Battery::Battery(Logger* newLogger, bool loglevelSet, QObject *parent) : QObject connect(settings, SIGNAL(resetTimers()), this, SLOT(resetTimers())); connect(highNotifyTimer, SIGNAL(timeout()), this, SLOT(showHighNotification())); connect(lowNotifyTimer, SIGNAL(timeout()), this, SLOT(showLowNotification())); + connect(healthNotifyTimer, SIGNAL(timeout()), this, SLOT(showHealthNotification())); updateData(); updateTimer->start(5000); @@ -105,7 +121,7 @@ Battery::Battery(Logger* newLogger, bool loglevelSet, QObject *parent) : QObject // aka. "charging" status didn't change // (or if both times are disabled, actually) // manually trigger the timer startup. - if(!highNotifyTimer->isActive() && !lowNotifyTimer->isActive()) { + if(!highNotifyTimer->isActive() && !lowNotifyTimer->isActive() && !healthNotifyTimer->isActive()) { resetTimers(); } } @@ -145,6 +161,29 @@ void Battery::updateData() stateFile->close(); } + if(temperatureFile && temperatureFile->open(QIODevice::ReadOnly)) { + nextTemperature = temperatureFile->readLine().trimmed().toInt(); + if(nextTemperature != temperature) { + temperature = nextTemperature; + emit temperatureChanged(temperature); + logV(QString("Temperature: %1°C").arg(temperature / 10)); + } + temperatureFile->close(); + } + + if(healthFile && healthFile->open(QIODevice::ReadOnly)) { + nextHealth = (QString(healthFile->readLine().trimmed().toLower())); + if(nextHealth != health) { + health = nextHealth; + emit healthChanged(health); + logV("Health: " + health); + + // Hide/show notification right away + resetTimers(); + } + healthFile->close(); + } + if(chargingEnabledFile && settings->getLimitEnabled()) { if(chargingEnabled && charge >= settings->getHighLimit()) { logD("Disabling charging..."); @@ -160,8 +199,10 @@ void Battery::updateData() void Battery::resetTimers() { highNotifyTimer->stop(); lowNotifyTimer->stop(); + healthNotifyTimer->stop(); highNotifyTimer->setInterval(settings->getHighNotificationsInterval() * 1000); lowNotifyTimer->setInterval(settings->getLowNotificationsInterval() * 1000); + healthNotifyTimer->setInterval(settings->getHealthNotificationsInterval() * 1000); if(settings->getHighNotificationsInterval() < 610) { logD("Starting high battery timer"); @@ -180,13 +221,22 @@ void Battery::resetTimers() { else { logD("Low battery timer not started"); } + + if(settings->getHealthNotificationsInterval() < 610) { + logD("Start health timer"); + healthNotifyTimer->start(); + showHealthNotification(); + } + else { + logD("Health timer not started"); + } } void Battery::showHighNotification() { if(settings->getHighNotificationsInterval() < 610 && (charge >= settings->getHighAlert() && state != "discharging") && !(charge == 100 && state == "idle")) { logV(QString("Notification: %1").arg(settings->getNotificationTitle().arg(charge))); - notification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationHighText(), settings->getHighAlertFile()); + chargeNotification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationHighText(), settings->getHighAlertFile()); if(settings->getHighNotificationsInterval() == 50) { logD("Stop high battery timer"); highNotifyTimer->stop(); @@ -194,14 +244,14 @@ void Battery::showHighNotification() { } else if(charge > settings->getLowAlert()) { logD("Close high battery notification"); - notification->close(); + chargeNotification->close(); } } void Battery::showLowNotification() { if(settings->getLowNotificationsInterval() < 610 && charge <= settings->getLowAlert() && state != "charging") { logV(QString("Notification: %1").arg(settings->getNotificationTitle().arg(charge))); - notification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationLowText(), settings->getLowAlertFile()); + chargeNotification->send(settings->getNotificationTitle().arg(charge), settings->getNotificationLowText(), settings->getLowAlertFile()); if(settings->getLowNotificationsInterval() == 50) { logD("Stop low battery timer"); lowNotifyTimer->stop(); @@ -209,7 +259,52 @@ void Battery::showLowNotification() { } else if(charge < settings->getHighAlert()) { logD("Close low battery notification"); - notification->close(); + chargeNotification->close(); + } +} + +void Battery::showHealthNotification() { + // set up alert categories + // TODO: manage this more globally, use better data types(?), align with QML/Settings part + static const QMap HealthThresh { + { "ok" , 0}, + { "warn" , 1}, + { "crit" , 2}, + }; + // map string values from sysfs file to alert category + static const QMap HealthState { + { "unknown" , HealthThresh["ok"] }, + { "good" , HealthThresh["ok"] }, + { "warm" , HealthThresh["warn"] }, + { "cool" , HealthThresh["warn"] }, + { "overheat" , HealthThresh["crit"] }, + { "cold" , HealthThresh["crit"] } + }; + if(settings->getHealthNotificationsInterval() < 610 && temperature != 0x7FFFFFFF && ( HealthState[health] != HealthThresh["ok"] && HealthState[health] >= settings->getHealthAlert() ) ) { + QString displayTemp = QString::number(temperature / 10.0); + if (QLocale().measurementSystem() == QLocale::ImperialUSSystem) + displayTemp = QString::number((temperature / 10) * 1.8 + 32) + " F"; + + QString titleArgs; + titleArgs = health + " (" + displayTemp + "), " + state; // might show other things in the future + + // show different test depending on severity + QString notificationText = ""; + if (HealthState[health] == HealthThresh["warn"]) { + notificationText = settings->getNotificationHealthWarnText(); + } else if (HealthState[health] == HealthThresh["crit"]) { + notificationText = settings->getNotificationHealthCritText(); + } + logD(QString("Notification: %1").arg(settings->getNotificationHealthTitle().arg(titleArgs))); + healthNotification->send(settings->getNotificationHealthTitle().arg(titleArgs), notificationText, settings->getHealthAlertFile()); + if(settings->getHealthNotificationsInterval() == 50) { + logD("Stop health timer"); + healthNotifyTimer->stop(); + } + } + else if(HealthState[health] == HealthThresh["ok"] || HealthState[health] < settings->getHealthAlert()) { + logD("Close health notification"); + healthNotification->close(); } } @@ -217,6 +312,10 @@ int Battery::getCharge() { return charge; } QString Battery::getState() { return state; } +int Battery::getTemperature() { return temperature; } + +QString Battery::getHealth() { return health; } + bool Battery::getChargingEnabled() { return chargingEnabled; } bool Battery::setChargingEnabled(const bool isEnabled) { @@ -253,7 +352,7 @@ bool Battery::getChargerConnected() { void Battery::shutdown() { logV("Shutting down..."); - notification->close(); + chargeNotification->close(); blockSignals(true); if(updateTimer) { updateTimer->stop(); @@ -267,6 +366,10 @@ void Battery::shutdown() { lowNotifyTimer->stop(); logD("Low battery notification stopped"); } + if(healthNotifyTimer) { + healthNotifyTimer->stop(); + logD("Health notification stopped"); + } // ENABLE/DISABLE CHARGING if(!setChargingEnabled(true) && !QHostInfo::localHostName().contains("SailfishEmul")) { logE("ERROR! Could not restore charger status! Your device " diff --git a/service/src/battery.h b/service/src/battery.h index 33533c7..cb45d2f 100644 --- a/service/src/battery.h +++ b/service/src/battery.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "settings.h" #include "mynotification.h" #include "logger.h" @@ -44,6 +45,9 @@ public: bool getChargingEnabled(); bool setChargingEnabled(const bool isEnabled); + int getTemperature(); + QString getHealth(); + public slots: void updateData(); void shutdown(); @@ -54,11 +58,16 @@ private: QFile *chargerConnectedFile = nullptr; QFile *stateFile = nullptr; QFile *chargingEnabledFile = nullptr; + QFile *temperatureFile = nullptr; + QFile *healthFile = nullptr; Settings *settings = nullptr; QTimer *updateTimer = nullptr; QTimer *highNotifyTimer = nullptr; QTimer *lowNotifyTimer = nullptr; - MyNotification *notification = nullptr; + QTimer *healthNotifyTimer = nullptr; + MyNotification *chargeNotification = nullptr; + MyNotification *healthNotification = nullptr; + // Default values: int charge = 100; // 100% full @@ -66,6 +75,9 @@ private: QString state = "idle"; // dis/charging, idle, unknown bool chargingEnabled = true; // Only ever disabled manually + QString health = "unknown"; // Good, warm, overheat. Might have Cold or Overvoltage depending on driver + int temperature = 0x7FFFFFFF; // This value means "unknown" (32-bit INT_MAX) + int enableChargingValue = 1; int disableChargingValue = 0; bool chargerIsEnabled = true; @@ -74,6 +86,8 @@ private: bool nextChargerConnected = chargerConnected; QString nextState = state; bool nextChargingEnabled = chargingEnabled; + int nextTemperature = temperature; + QString nextHealth = health; QFileDevice::Permissions originalPerms; // Updated in constructor QFileDevice::Permissions customPerms = static_cast(0x0666); @@ -83,11 +97,14 @@ signals: void stateChanged(QString); void chargingEnabledChanged(bool); void chargerConnectedChanged(bool); + void temperatureChanged(int); + void healthChanged(QString); public slots: void resetTimers(); void showHighNotification(); void showLowNotification(); + void showHealthNotification(); }; #endif // BATTERY_H diff --git a/service/src/settings.cpp b/service/src/settings.cpp index 6f6d990..ac206c0 100644 --- a/service/src/settings.cpp +++ b/service/src/settings.cpp @@ -92,6 +92,9 @@ Settings::Settings(Logger* newLogger, QObject *parent) : QObject(parent) notificationTitle = "Battery charge %1%"; notificationLowText = "Please connect the charger."; notificationHighText = "Please disconnect the charger."; + notificationHealthTitle = "Battery health %1"; + notificationHealthWarnText = "Battery health is not good"; + notificationHealthCritText = "Battery health is critical"; // Do this here, because... watcher = new QFileSystemWatcher(QStringList(mySettings->fileName()), this); @@ -129,8 +132,10 @@ void Settings::updateConfig(const QString path) { loadInteger(sLowAlert, lowAlert, 5, 99); loadInteger(sHighAlert, highAlert, 6, 100); + loadInteger(sHealthAlert, healthAlert, 0, 2); restartTimers |= loadInteger(sHighNotificationsInterval, highNotificationsInterval, 50, 610); restartTimers |= loadInteger(sLowNotificationsInterval, lowNotificationsInterval, 50, 610); + restartTimers |= loadInteger(sHealthNotificationsInterval, healthNotificationsInterval, 50, 610); loadInteger(sLimitEnabled, limitEnabled, 0, 1); loadInteger(sLowLimit, lowLimit, 5, 99); loadInteger(sHighLimit, highLimit, 6, 100); @@ -139,6 +144,11 @@ void Settings::updateConfig(const QString path) { notificationLowText = mySettings->value(sNotificationLowText, notificationLowText).toString(); notificationHighText = mySettings->value(sNotificationHighText, notificationHighText).toString(); + notificationHealthTitle = mySettings->value(sNotificationHealthTitle, notificationHealthTitle).toString(); + notificationHealthWarnText = mySettings->value(sNotificationHealthWarnText, notificationHealthWarnText).toString(); + notificationHealthCritText = mySettings->value(sNotificationHealthCritText, notificationHealthCritText).toString(); + + // Update log level int oldLogLevel = logLevel; loadInteger(sLogLevel, logLevel, 0, 2); @@ -168,16 +178,22 @@ void Settings::updateConfig(const QString path) { } // Getters condensed -int Settings::getLowAlert() { return lowAlert; } -int Settings::getHighAlert() { return highAlert; } -int Settings::getHighNotificationsInterval() { return highNotificationsInterval; } -int Settings::getLowNotificationsInterval() { return lowNotificationsInterval; } -int Settings::getLowLimit() { return lowLimit; } -int Settings::getHighLimit() { return highLimit; } -bool Settings::getLimitEnabled() { return limitEnabled == 1; } -QString Settings::getLowAlertFile() { return lowAlertFile; } -QString Settings::getHighAlertFile() { return highAlertFile; } -QString Settings::getNotificationTitle() { return notificationTitle; } -QString Settings::getNotificationLowText() { return notificationLowText; } -QString Settings::getNotificationHighText() { return notificationHighText; } -int Settings::getLogLevel() { return logLevel; } +int Settings::getLowAlert() { return lowAlert; } +int Settings::getHighAlert() { return highAlert; } +int Settings::getHealthAlert() { return healthAlert; } +int Settings::getHighNotificationsInterval() { return highNotificationsInterval; } +int Settings::getLowNotificationsInterval() { return lowNotificationsInterval; } +int Settings::getHealthNotificationsInterval() { return healthNotificationsInterval; } +int Settings::getLowLimit() { return lowLimit; } +int Settings::getHighLimit() { return highLimit; } +bool Settings::getLimitEnabled() { return limitEnabled == 1; } +QString Settings::getLowAlertFile() { return lowAlertFile; } +QString Settings::getHighAlertFile() { return highAlertFile; } +QString Settings::getHealthAlertFile() { return healthAlertFile; } +QString Settings::getNotificationTitle() { return notificationTitle; } +QString Settings::getNotificationLowText() { return notificationLowText; } +QString Settings::getNotificationHighText() { return notificationHighText; } +QString Settings::getNotificationHealthTitle() { return notificationHealthTitle; } +QString Settings::getNotificationHealthWarnText() { return notificationHealthWarnText; } +QString Settings::getNotificationHealthCritText() { return notificationHealthCritText; } +int Settings::getLogLevel() { return logLevel; } diff --git a/service/src/settings.h b/service/src/settings.h index a7e4320..55c39f3 100644 --- a/service/src/settings.h +++ b/service/src/settings.h @@ -36,17 +36,24 @@ public: int getHighAlert(); int getHighNotificationsInterval(); int getLowNotificationsInterval(); + int getHealthAlert(); + int getHealthNotificationsInterval(); int getLowLimit(); int getHighLimit(); int getLogLevel(); bool getLimitEnabled(); bool getHighNotificationsEnabled(); bool getLowNotificationsEnabled(); + bool getHealthNotificationsEnabled(); QString getLowAlertFile(); QString getHighAlertFile(); + QString getHealthAlertFile(); QString getNotificationTitle(); QString getNotificationLowText(); QString getNotificationHighText(); + QString getNotificationHealthTitle(); + QString getNotificationHealthWarnText(); + QString getNotificationHealthCritText(); private: Logger* logger; @@ -63,8 +70,10 @@ private: // Default values int lowAlert = 25; int highAlert = 75; + int healthAlert = 1; // 0=off, 1=warn, 2=crit int highNotificationsInterval = 60; int lowNotificationsInterval = 60; + int healthNotificationsInterval = 60; // Converted to boolean for QML int limitEnabled = 1; @@ -74,23 +83,33 @@ private: 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"; + QString healthAlertFile = "/usr/share/sounds/jolla-ambient/stereo/battery_low.wav"; QString notificationTitle; QString notificationLowText; QString notificationHighText; + QString notificationHealthTitle; + QString notificationHealthWarnText; + QString notificationHealthCritText; // To avoid repeating the same string over and over and over... const char* sLowAlert = "lowAlert"; const char* sHighAlert = "highAlert"; + const char* sHealthAlert = "healthAlert"; const char* sHighNotificationsInterval = "highNotificationsInterval"; const char* sLowNotificationsInterval = "lowNotificationsInterval"; + const char* sHealthNotificationsInterval = "healthNotificationsInterval"; const char* sLimitEnabled = "limitEnabled"; const char* sLowLimit = "lowLimit"; const char* sHighLimit = "highLimit"; const char* sLowAlertFile = "lowAlertFile"; const char* sHighAlertFile = "highAlertFile"; + const char* sHealthAlertFile = "healthAlertFile"; const char* sNotificationTitle = "notificationTitle"; const char* sNotificationLowText = "notificationLowText"; const char* sNotificationHighText = "notificationHighText"; + const char* sNotificationHealthTitle = "notificationHealthTitle"; + const char* sNotificationHealthWarnText = "notificationHealthWarnText"; + const char* sNotificationHealthCritText = "notificationHealthCritText"; const char* sLogFilename = "logFilename"; const char* sLogLevel = "logLevel";