diff --git a/PowerDevilSettings.kcfg b/PowerDevilSettings.kcfg
--- a/PowerDevilSettings.kcfg
+++ b/PowerDevilSettings.kcfg
@@ -31,5 +31,8 @@
2
+
+ 10
+
diff --git a/daemon/powerdevilcore.h b/daemon/powerdevilcore.h
--- a/daemon/powerdevilcore.h
+++ b/daemon/powerdevilcore.h
@@ -63,7 +63,10 @@
void emitNotification(const QString &evid, const QString &message = QString(),
const QString &iconname = QString());
void emitRichNotification(const QString &evid, const QString &title, const QString &message = QString());
- bool emitBatteryChargePercentNotification(int currentPercent, int previousPercent);
+
+ void emitNotification(const QString &eventId, const QString &title, const QString &message, const QString &iconName);
+
+ bool emitBatteryChargePercentNotification(int currentPercent, int previousPercent, const QString &udi = QString());
BackendInterface *backend();
@@ -106,16 +109,17 @@
friend class Action;
BackendInterface *m_backend;
- QStringList m_loadedBatteriesUdi;
QDBusServiceWatcher *m_notificationsWatcher;
+ bool m_notificationsReady = false;
KSharedConfigPtr m_profilesConfig;
QString m_currentProfile;
- QHash< QString, int > m_batteriesPercent;
- QHash< QString, bool > m_batteriesCharged;
+ QHash m_batteriesPercent;
+ QHash m_peripheralBatteriesPercent;
+ QHash m_batteriesCharged;
QTimer *m_criticalBatteryTimer;
QPointer m_criticalBatteryNotification;
diff --git a/daemon/powerdevilcore.cpp b/daemon/powerdevilcore.cpp
--- a/daemon/powerdevilcore.cpp
+++ b/daemon/powerdevilcore.cpp
@@ -239,7 +239,7 @@
profileId = activity;
} else {
// It doesn't, let's load the current state's profile
- if (m_loadedBatteriesUdi.isEmpty()) {
+ if (m_batteriesPercent.isEmpty()) {
qCDebug(POWERDEVIL) << "No batteries found, loading AC";
profileId = "AC";
} else if (activityConfig.readEntry("mode", "None") == "ActLike") {
@@ -377,83 +377,75 @@
}
}
-void Core::onDeviceAdded(const QString& udi)
+void Core::onDeviceAdded(const QString &udi)
{
- if (m_loadedBatteriesUdi.contains(udi)) {
+ if (m_batteriesPercent.contains(udi) || m_peripheralBatteriesPercent.contains(udi)) {
// We already know about this device
return;
}
using namespace Solid;
Device device(udi);
- Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery));
+ Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery));
if (!b) {
- // Not interesting to us
return;
}
- if (b->type() != Solid::Battery::PrimaryBattery && b->type() != Solid::Battery::UpsBattery) {
- // Not interesting to us
- return;
- }
-
- if (!b->isPowerSupply()) {
- // TODO: At some later point it would be really nice to handle those batteries too
- // eg, show "your mouse is running low", but in the mean time, we don't care about those
- return;
- }
-
- if (!connect(b, SIGNAL(chargePercentChanged(int,QString)),
- this, SLOT(onBatteryChargePercentChanged(int,QString))) ||
- !connect(b, SIGNAL(chargeStateChanged(int,QString)),
- this, SLOT(onBatteryChargeStateChanged(int,QString)))) {
+ if (!connect(b, &Battery::chargePercentChanged, this, &Core::onBatteryChargePercentChanged) ||
+ !connect(b, &Battery::chargeStateChanged, this, &Core::onBatteryChargeStateChanged)) {
emitNotification("powerdevilerror", i18n("Could not connect to battery interface.\n"
"Please check your system configuration"));
}
- qCDebug(POWERDEVIL) << "A new battery was detected";
+ qCDebug(POWERDEVIL) << "Battery with UDI" << udi << "was detected";
- m_batteriesPercent[udi] = b->chargePercent();
- m_batteriesCharged[udi] = (b->chargeState() == Solid::Battery::FullyCharged);
- m_loadedBatteriesUdi.append(udi);
+ if (b->isPowerSupply()) {
+ m_batteriesPercent[udi] = b->chargePercent();
+ m_batteriesCharged[udi] = (b->chargeState() == Solid::Battery::FullyCharged);
+ } else { // non-power supply batteries are treated separately
+ m_peripheralBatteriesPercent[udi] = b->chargePercent();
- const int chargePercent = currentChargePercent();
+ // notify the user about the empty mouse/keyboard when plugging it in; don't when
+ // notifications aren't ready yet so we avoid showing them ontop of ksplash;
+ // also we'll notify about all devices when notifications are ready anyway
+ if (m_notificationsReady) {
+ emitBatteryChargePercentNotification(b->chargePercent(), 1000 /* so current is always lower than previous */, udi);
+ }
+ }
// If a new battery has been added, let's clear some pending suspend actions if the new global batteries percentage is
// higher than the battery critical level. (See bug 329537)
- if (m_criticalBatteryTimer->isActive() && chargePercent > PowerDevilSettings::batteryCriticalLevel()) {
+ if (m_criticalBatteryTimer->isActive() && currentChargePercent() > PowerDevilSettings::batteryCriticalLevel()) {
m_criticalBatteryTimer->stop();
if (m_criticalBatteryNotification) {
m_criticalBatteryNotification->close();
}
emitRichNotification("pluggedin",
i18n("Extra Battery Added"),
- i18n("All pending suspend actions have been canceled."));
+ i18n("All pending suspend actions have been canceled.")); // FIXME This wording is too technical
}
}
-void Core::onDeviceRemoved(const QString& udi)
+void Core::onDeviceRemoved(const QString &udi)
{
- if (!m_loadedBatteriesUdi.contains(udi)) {
+ if (!m_batteriesPercent.contains(udi) && !m_peripheralBatteriesPercent.contains(udi)) {
// We don't know about this device
return;
}
using namespace Solid;
Device device(udi);
- Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery));
+ Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery));
- disconnect(b, SIGNAL(chargePercentChanged(int,QString)),
- this, SLOT(onBatteryChargePercentChanged(int,QString)));
- disconnect(b, SIGNAL(chargeStateChanged(int,QString)),
- this, SLOT(onBatteryChargeStateChanged(int,QString)));
+ disconnect(b, &Battery::chargePercentChanged, this, &Core::onBatteryChargePercentChanged);
+ disconnect(b, &Battery::chargeStateChanged, this, &Core::onBatteryChargeStateChanged);
- qCDebug(POWERDEVIL) << "An existing battery has been removed";
+ qCDebug(POWERDEVIL) << "Battery with UDI" << udi << "has been removed";
m_batteriesPercent.remove(udi);
+ m_peripheralBatteriesPercent.remove(udi);
m_batteriesCharged.remove(udi);
- m_loadedBatteriesUdi.removeOne(udi);
}
void Core::emitNotification(const QString &evid, const QString &message, const QString &iconname)
@@ -467,14 +459,74 @@
}
}
+void Core::emitNotification(const QString &eventId, const QString &title, const QString &message, const QString &iconName)
+{
+ KNotification::event(eventId, title, message, iconName, 0, KNotification::CloseOnTimeout, "powerdevil");
+}
+
void Core::emitRichNotification(const QString &evid, const QString &title, const QString &message)
{
KNotification::event(evid, title, message, QPixmap(),
0, KNotification::CloseOnTimeout, "powerdevil");
}
-bool Core::emitBatteryChargePercentNotification(int currentPercent, int previousPercent)
+bool Core::emitBatteryChargePercentNotification(int currentPercent, int previousPercent, const QString &udi)
{
+ using namespace Solid;
+ Device device(udi);
+ Battery *b = qobject_cast(device.asDeviceInterface(DeviceInterface::Battery));
+
+ if (b && !b->isPowerSupply()) {
+ // if you leave the device out of reach or it has not been initialized yet
+ // it won't be "there" and report 0%, don't show anything in this case
+ if (!b->isPresent()) {
+ return false;
+ }
+
+ if (currentPercent <= PowerDevilSettings::peripheralBatteryLowLevel() &&
+ previousPercent > PowerDevilSettings::peripheralBatteryLowLevel()) {
+
+ QString name = device.product();
+ if (!device.vendor().isEmpty()) {
+ name = i18nc("%1 is vendor name, %2 is product name", "%1 %2", device.vendor(), device.product());
+ }
+
+ QString title;
+ QString msg;
+ QString icon;
+
+ switch(b->type()) {
+ case Battery::MouseBattery:
+ title = i18n("Mouse Battery Low (%1% Remaining)", currentPercent);
+ msg = i18nc("Placeholder is device name",
+ "The battery in your mouse (\"%1\") is low, and the device may turn itself off at any time. "
+ "Please replace or charge the battery as soon as possible.", name);
+ icon = QStringLiteral("input-mouse");
+ break;
+ case Battery::KeyboardBattery:
+ title = i18n("Keyboard Battery Low (%1% Remaining)", currentPercent);
+ msg = i18nc("Placeholder is device name",
+ "The battery in your keyboard (\"%1\") is low, and the device may turn itself off at any time. "
+ "Please replace or charge the battery as soon as possible.", name);
+ icon = QStringLiteral("input-keyboard");
+ break;
+ default:
+ title = i18nc("The battery in an external device", "Device Battery Low (%1% Remaining)", currentPercent);
+ msg = i18nc("Placeholder is device name",
+ "The battery in a connected device (\"%1\") is low, and the device may turn itself off at any time. "
+ "Please replace or charge the battery as soon as possible.", name);
+ icon = QStringLiteral("battery-caution");
+ break;
+ }
+
+ emitNotification("lowperipheralbattery", title, msg, icon);
+
+ return true;
+ }
+
+ return false;
+ }
+
if (m_backend->acAdapterState() == BackendInterface::Plugged) {
return false;
}
@@ -565,23 +617,37 @@
void Core::onBatteryChargePercentChanged(int percent, const QString &udi)
{
+ if (m_peripheralBatteriesPercent.contains(udi)) {
+ const int previousPercent = m_peripheralBatteriesPercent.value(udi);
+ m_peripheralBatteriesPercent[udi] = percent;
+
+ if (percent < previousPercent) {
+ emitBatteryChargePercentNotification(percent, previousPercent, udi);
+ }
+ return;
+ }
+
// Compute the previous and current global percentage
const int previousPercent = currentChargePercent();
const int currentPercent = previousPercent - (m_batteriesPercent[udi] - percent);
// Update the battery percentage
m_batteriesPercent[udi] = percent;
if (currentPercent < previousPercent) {
- if (emitBatteryChargePercentNotification(currentPercent, previousPercent)) {
+ if (emitBatteryChargePercentNotification(currentPercent, previousPercent, udi)) {
// Only refresh status if a notification has actually been emitted
loadProfile();
}
}
}
void Core::onBatteryChargeStateChanged(int state, const QString &udi)
{
+ if (!m_batteriesCharged.contains(udi)) {
+ return;
+ }
+
bool previousCharged = true;
for (auto i = m_batteriesCharged.constBegin(); i != m_batteriesCharged.constEnd(); ++i) {
if (!i.value()) {
@@ -712,19 +778,31 @@
{
Q_UNUSED(service);
- static bool notificationsReady = false;
- if (notificationsReady) {
+ if (m_notificationsReady) {
return;
}
+ bool needsRefresh = false;
+
// show warning about low batteries right on session startup, force it to show
// by making sure the "old" percentage (that magic number) is always higher than the current one
if (emitBatteryChargePercentNotification(currentChargePercent(), 1000)) {
- // need to refresh status to prevent the notification from showing again when charge percentage changes
+ needsRefresh = true;
+ }
+
+ // now also emit notifications for all peripheral batteries
+ for (auto it = m_peripheralBatteriesPercent.constBegin(), end = m_peripheralBatteriesPercent.constEnd(); it != end; ++it) {
+ if (emitBatteryChargePercentNotification(it.value() /*currentPercent*/, 1000, it.key() /*udi*/)) {
+ needsRefresh = true;
+ }
+ }
+
+ // need to refresh status to prevent the notification from showing again when charge percentage changes
+ if (needsRefresh) {
refreshStatus();
}
- notificationsReady = true;
+ m_notificationsReady = true;
if (m_notificationsWatcher) {
delete m_notificationsWatcher;
diff --git a/kcmodule/global/GeneralPage.cpp b/kcmodule/global/GeneralPage.cpp
--- a/kcmodule/global/GeneralPage.cpp
+++ b/kcmodule/global/GeneralPage.cpp
@@ -89,13 +89,15 @@
void GeneralPage::fillUi()
{
- bool hasBattery = false;
+ bool hasPowerSupplyBattery = false;
+ bool hasPeripheralBattery = false;
- Q_FOREACH(const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString())) {
+ Q_FOREACH (const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::Battery, QString())) {
const Solid::Battery *b = qobject_cast (device.asDeviceInterface(Solid::DeviceInterface::Battery));
- if(b->type() == Solid::Battery::PrimaryBattery || b->type() == Solid::Battery::UpsBattery) {
- hasBattery = true;
- break;
+ if (b->isPowerSupply()) {
+ hasPowerSupplyBattery = true;
+ } else {
+ hasPeripheralBattery = true;
}
}
@@ -118,26 +120,34 @@
connect(lowSpin, SIGNAL(valueChanged(int)), SLOT(changed()));
connect(criticalSpin, SIGNAL(valueChanged(int)), SLOT(changed()));
+ connect(lowPeripheralSpin, SIGNAL(valueChanged(int)), SLOT(changed()));
connect(BatteryCriticalCombo, SIGNAL(currentIndexChanged(int)), SLOT(changed()));
- // Disable stuff, eventually
- if (!hasBattery) {
- batteryLevelsLabel->hide();
-
+ if (!hasPowerSupplyBattery) {
BatteryCriticalLabel->hide();
BatteryCriticalCombo->hide();
lowLabel->hide();
lowSpin->hide();
criticalLabel->hide();
criticalSpin->hide();
}
+
+ if (!hasPeripheralBattery) {
+ lowPeripheralLabel->hide();
+ lowPeripheralSpin->hide();
+ }
+
+ if (!hasPeripheralBattery && !hasPeripheralBattery) {
+ batteryLevelsLabel->hide();
+ }
}
void GeneralPage::load()
{
lowSpin->setValue(PowerDevilSettings::batteryLowLevel());
criticalSpin->setValue(PowerDevilSettings::batteryCriticalLevel());
+ lowPeripheralSpin->setValue(PowerDevilSettings::peripheralBatteryLowLevel());
BatteryCriticalCombo->setCurrentIndex(BatteryCriticalCombo->findData(PowerDevilSettings::batteryCriticalAction()));
}
@@ -151,6 +161,7 @@
{
PowerDevilSettings::setBatteryLowLevel(lowSpin->value());
PowerDevilSettings::setBatteryCriticalLevel(criticalSpin->value());
+ PowerDevilSettings::setPeripheralBatteryLowLevel(lowPeripheralSpin->value());
PowerDevilSettings::setBatteryCriticalAction(BatteryCriticalCombo->itemData(BatteryCriticalCombo->currentIndex()).toInt());
diff --git a/kcmodule/global/generalPage.ui b/kcmodule/global/generalPage.ui
--- a/kcmodule/global/generalPage.ui
+++ b/kcmodule/global/generalPage.ui
@@ -113,7 +113,7 @@
- -
+
-
Qt::Vertical
@@ -126,7 +126,7 @@
- -
+
-
@@ -145,6 +145,23 @@
+ -
+
+
+ Low level for peripheral devices:
+
+
+
+ -
+
+
+ %
+
+
+ 100
+
+
+
-
diff --git a/powerdevil.notifyrc b/powerdevil.notifyrc
--- a/powerdevil.notifyrc
+++ b/powerdevil.notifyrc
@@ -650,6 +650,14 @@
Action=
IconName=battery-100
+[Event/lowperipheralbattery]
+Name=Peripheral Battery Low
+Comment=The battery in a connected device, such as mouse or keyboard, is low
+Contexts=warningnot
+Sound=Oxygen-Sys-Warning.ogg
+Action=Sound|Popup
+IconName=battery-caution
+
[Event/pluggedin]
Name=AC adaptor plugged in
Name[ar]=وُصِلَ محوِّل الكهرباء