File indexing completed on 2024-05-19 05:37:52

0001 /*
0002     SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2007-2008 Sebastian Kuegler <sebas@kde.org>
0004     SPDX-FileCopyrightText: 2007 Maor Vanmak <mvanmak1@gmail.com>
0005     SPDX-FileCopyrightText: 2008 Dario Freddi <drf54321@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "powermanagementengine.h"
0011 
0012 // kde-workspace/libs
0013 #include <sessionmanagement.h>
0014 
0015 // solid specific includes
0016 #include <solid/device.h>
0017 #include <solid/deviceinterface.h>
0018 #include <solid/devicenotifier.h>
0019 
0020 #include <KAuthorized>
0021 #include <KIdleTime>
0022 #include <KService>
0023 #include <klocalizedstring.h>
0024 
0025 #include <QDebug>
0026 
0027 #include <QDBusConnectionInterface>
0028 #include <QDBusError>
0029 #include <QDBusInterface>
0030 #include <QDBusMetaType>
0031 #include <QDBusPendingCallWatcher>
0032 #include <QDBusReply>
0033 #include <QIcon>
0034 
0035 #include "powermanagementservice.h"
0036 #include <Plasma5Support/DataContainer>
0037 
0038 static const char SOLID_POWERMANAGEMENT_SERVICE[] = "org.kde.Solid.PowerManagement";
0039 static const char FDO_POWERMANAGEMENT_SERVICE[] = "org.freedesktop.PowerManagement";
0040 
0041 namespace
0042 {
0043 template<typename ReplyType>
0044 void createAsyncDBusMethodCallAndCallback(QObject *parent,
0045                                           const QString &destination,
0046                                           const QString &path,
0047                                           const QString &interface,
0048                                           const QString &method,
0049                                           std::function<void(ReplyType)> &&callback)
0050 {
0051     QDBusMessage msg = QDBusMessage::createMethodCall(destination, path, interface, method);
0052     QDBusPendingReply<ReplyType> reply = QDBusConnection::sessionBus().asyncCall(msg);
0053     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, parent);
0054     parent->connect(watcher, &QDBusPendingCallWatcher::finished, parent, [callback](QDBusPendingCallWatcher *watcher) {
0055         QDBusPendingReply<ReplyType> reply = *watcher;
0056         if (!reply.isError()) {
0057             callback(reply.value());
0058         }
0059         watcher->deleteLater();
0060     });
0061 }
0062 }
0063 
0064 PowermanagementEngine::PowermanagementEngine(QObject *parent)
0065     : Plasma5Support::DataEngine(parent)
0066     , m_sources(basicSourceNames())
0067     , m_session(new SessionManagement(this))
0068 {
0069     qDBusRegisterMetaType<QList<InhibitionInfo>>();
0070     qDBusRegisterMetaType<InhibitionInfo>();
0071     qDBusRegisterMetaType<QList<QVariant>>();
0072     qDBusRegisterMetaType<QList<QVariantMap>>();
0073     init();
0074 }
0075 
0076 PowermanagementEngine::~PowermanagementEngine()
0077 {
0078 }
0079 
0080 void PowermanagementEngine::init()
0081 {
0082     connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, &PowermanagementEngine::deviceAdded);
0083     connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, &PowermanagementEngine::deviceRemoved);
0084 
0085     if (QDBusConnection::sessionBus().interface()->isServiceRegistered(SOLID_POWERMANAGEMENT_SERVICE)) {
0086         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0087                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"),
0088                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"),
0089                                                    QStringLiteral("brightnessChanged"),
0090                                                    this,
0091                                                    SLOT(screenBrightnessChanged(int)))) {
0092             qDebug() << "error connecting to Brightness changes via dbus";
0093         }
0094 
0095         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0096                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"),
0097                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"),
0098                                                    QStringLiteral("brightnessMaxChanged"),
0099                                                    this,
0100                                                    SLOT(maximumScreenBrightnessChanged(int)))) {
0101             qDebug() << "error connecting to max brightness changes via dbus";
0102         }
0103 
0104         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0105                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/KeyboardBrightnessControl"),
0106                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.KeyboardBrightnessControl"),
0107                                                    QStringLiteral("keyboardBrightnessChanged"),
0108                                                    this,
0109                                                    SLOT(keyboardBrightnessChanged(int)))) {
0110             qDebug() << "error connecting to Keyboard Brightness changes via dbus";
0111         }
0112 
0113         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0114                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/KeyboardBrightnessControl"),
0115                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.KeyboardBrightnessControl"),
0116                                                    QStringLiteral("keyboardBrightnessMaxChanged"),
0117                                                    this,
0118                                                    SLOT(maximumKeyboardBrightnessChanged(int)))) {
0119             qDebug() << "error connecting to max keyboard Brightness changes via dbus";
0120         }
0121 
0122         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0123                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/HandleButtonEvents"),
0124                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.HandleButtonEvents"),
0125                                                    QStringLiteral("triggersLidActionChanged"),
0126                                                    this,
0127                                                    SLOT(triggersLidActionChanged(bool)))) {
0128             qDebug() << "error connecting to lid action trigger changes via dbus";
0129         }
0130 
0131         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0132                                                    QStringLiteral("/org/kde/Solid/PowerManagement/PolicyAgent"),
0133                                                    QStringLiteral("org.kde.Solid.PowerManagement.PolicyAgent"),
0134                                                    QStringLiteral("InhibitionsChanged"),
0135                                                    this,
0136                                                    SLOT(inhibitionsChanged(QList<InhibitionInfo>, QStringList)))) {
0137             qDebug() << "error connecting to inhibition changes via dbus";
0138         }
0139 
0140         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0141                                                    QStringLiteral("/org/kde/Solid/PowerManagement"),
0142                                                    SOLID_POWERMANAGEMENT_SERVICE,
0143                                                    QStringLiteral("batteryRemainingTimeChanged"),
0144                                                    this,
0145                                                    SLOT(batteryRemainingTimeChanged(qulonglong)))) {
0146             qDebug() << "error connecting to remaining time changes";
0147         }
0148 
0149         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0150                                                    QStringLiteral("/org/kde/Solid/PowerManagement"),
0151                                                    SOLID_POWERMANAGEMENT_SERVICE,
0152                                                    QStringLiteral("smoothedBatteryRemainingTimeChanged"),
0153                                                    this,
0154                                                    SLOT(smoothedBatteryRemainingTimeChanged(qulonglong)))) {
0155             qDebug() << "error connecting to smoothed remaining time changes";
0156         }
0157 
0158         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0159                                                    QStringLiteral("/org/kde/Solid/PowerManagement"),
0160                                                    SOLID_POWERMANAGEMENT_SERVICE,
0161                                                    QStringLiteral("chargeStopThresholdChanged"),
0162                                                    this,
0163                                                    SLOT(chargeStopThresholdChanged(int)))) {
0164             qDebug() << "error connecting to charge stop threshold changes via dbus";
0165         }
0166 
0167         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0168                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0169                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0170                                                    QStringLiteral("currentProfileChanged"),
0171                                                    this,
0172                                                    SLOT(updatePowerProfileCurrentProfile(QString)))) {
0173             qDebug() << "error connecting to current profile changes via dbus";
0174         }
0175 
0176         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0177                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0178                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0179                                                    QStringLiteral("profileChoicesChanged"),
0180                                                    this,
0181                                                    SLOT(updatePowerProfileChoices(QStringList)))) {
0182             qDebug() << "error connecting to profile choices changes via dbus";
0183         }
0184 
0185         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0186                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0187                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0188                                                    QStringLiteral("performanceInhibitedReasonChanged"),
0189                                                    this,
0190                                                    SLOT(updatePowerProfilePerformanceInhibitedReason(QString)))) {
0191             qDebug() << "error connecting to inhibition reason changes via dbus";
0192         }
0193 
0194         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0195                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0196                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0197                                                    QStringLiteral("performanceDegradedReasonChanged"),
0198                                                    this,
0199                                                    SLOT(updatePowerProfilePerformanceDegradedReason(QString)))) {
0200             qDebug() << "error connecting to degradation reason changes via dbus";
0201         }
0202 
0203         if (!QDBusConnection::sessionBus().connect(SOLID_POWERMANAGEMENT_SERVICE,
0204                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0205                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0206                                                    QStringLiteral("profileHoldsChanged"),
0207                                                    this,
0208                                                    SLOT(updatePowerProfileHolds(QList<QVariantMap>)))) {
0209             qDebug() << "error connecting to profile hold changes via dbus";
0210         }
0211     }
0212 
0213     if (QDBusConnection::sessionBus().interface()->isServiceRegistered(FDO_POWERMANAGEMENT_SERVICE)) {
0214         if (!QDBusConnection::sessionBus().connect(FDO_POWERMANAGEMENT_SERVICE,
0215                                                    QStringLiteral("/org/freedesktop/PowerManagement"),
0216                                                    QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0217                                                    QStringLiteral("HasInhibitChanged"),
0218                                                    this,
0219                                                    SLOT(hasInhibitionChanged(bool)))) {
0220             qDebug() << "error connecting to fdo inhibition changes via dbus";
0221         }
0222     }
0223 }
0224 
0225 QStringList PowermanagementEngine::basicSourceNames() const
0226 {
0227     QStringList sources;
0228     sources << QStringLiteral("Battery") << QStringLiteral("AC Adapter") << QStringLiteral("Sleep States") << QStringLiteral("PowerDevil")
0229             << QStringLiteral("Inhibitions") << QStringLiteral("Power Profiles") << QStringLiteral("PowerManagement");
0230     return sources;
0231 }
0232 
0233 QStringList PowermanagementEngine::sources() const
0234 {
0235     return m_sources;
0236 }
0237 
0238 bool PowermanagementEngine::sourceRequestEvent(const QString &name)
0239 {
0240     if (name == QLatin1String("Battery")) {
0241         const QList<Solid::Device> listBattery = Solid::Device::listFromType(Solid::DeviceInterface::Battery);
0242         m_batterySources.clear();
0243 
0244         if (listBattery.isEmpty()) {
0245             setData(QStringLiteral("Battery"), QStringLiteral("Has Battery"), false);
0246             setData(QStringLiteral("Battery"), QStringLiteral("Has Cumulative"), false);
0247             return true;
0248         }
0249 
0250         uint index = 0;
0251         QStringList batterySources;
0252 
0253         for (const Solid::Device &deviceBattery : listBattery) {
0254             const Solid::Battery *battery = deviceBattery.as<Solid::Battery>();
0255 
0256             const QString source = QStringLiteral("Battery%1").arg(index++);
0257 
0258             batterySources << source;
0259             m_batterySources[deviceBattery.udi()] = source;
0260 
0261             connect(battery, &Solid::Battery::chargeStateChanged, this, &PowermanagementEngine::updateBatteryChargeState);
0262             connect(battery, &Solid::Battery::chargePercentChanged, this, &PowermanagementEngine::updateBatteryChargePercent);
0263             connect(battery, &Solid::Battery::energyChanged, this, &PowermanagementEngine::updateBatteryEnergy);
0264             connect(battery, &Solid::Battery::presentStateChanged, this, &PowermanagementEngine::updateBatteryPresentState);
0265 
0266             // Set initial values
0267             updateBatteryChargeState(battery->chargeState(), deviceBattery.udi());
0268             updateBatteryChargePercent(battery->chargePercent(), deviceBattery.udi());
0269             updateBatteryEnergy(battery->energy(), deviceBattery.udi());
0270             updateBatteryPresentState(battery->isPresent(), deviceBattery.udi());
0271             updateBatteryPowerSupplyState(battery->isPowerSupply(), deviceBattery.udi());
0272 
0273             setData(source, QStringLiteral("Vendor"), deviceBattery.vendor());
0274             setData(source, QStringLiteral("Product"), deviceBattery.product());
0275             setData(source, QStringLiteral("Capacity"), battery->capacity());
0276             setData(source, QStringLiteral("Type"), batteryTypeToString(battery));
0277         }
0278 
0279         updateBatteryNames();
0280         updateOverallBattery();
0281 
0282         setData(QStringLiteral("Battery"), QStringLiteral("Sources"), batterySources);
0283         setData(QStringLiteral("Battery"), QStringLiteral("Has Battery"), !batterySources.isEmpty());
0284         if (!batterySources.isEmpty()) {
0285             createPowerManagementDBusMethodCallAndNotifyChanged<qulonglong>(
0286                 QStringLiteral("batteryRemainingTime"),
0287                 std::bind(&PowermanagementEngine::batteryRemainingTimeChanged, this, std::placeholders::_1));
0288             createPowerManagementDBusMethodCallAndNotifyChanged<qulonglong>(
0289                 QStringLiteral("smoothedBatteryRemainingTime"),
0290                 std::bind(&PowermanagementEngine::smoothedBatteryRemainingTimeChanged, this, std::placeholders::_1));
0291         }
0292 
0293         createPowerManagementDBusMethodCallAndNotifyChanged<int>(QStringLiteral("chargeStopThreshold"),
0294                                                                  std::bind(&PowermanagementEngine::chargeStopThresholdChanged, this, std::placeholders::_1));
0295 
0296         m_sources = basicSourceNames() + batterySources;
0297     } else if (name == QLatin1String("AC Adapter")) {
0298         QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.PowerManagement"),
0299                                               QStringLiteral("/org/freedesktop/PowerManagement"),
0300                                               QStringLiteral("org.freedesktop.PowerManagement"),
0301                                               QStringLiteral("PowerSaveStatusChanged"),
0302                                               this,
0303                                               SLOT(updateAcPlugState(bool)));
0304 
0305         QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.PowerManagement"),
0306                                                           QStringLiteral("/org/freedesktop/PowerManagement"),
0307                                                           QStringLiteral("org.freedesktop.PowerManagement"),
0308                                                           QStringLiteral("GetPowerSaveStatus"));
0309         QDBusReply<bool> reply = QDBusConnection::sessionBus().call(msg);
0310         updateAcPlugState(reply.isValid() ? reply.value() : false);
0311     } else if (name == QLatin1String("Sleep States")) {
0312         setData(QStringLiteral("Sleep States"), QStringLiteral("Standby"), m_session->canSuspend());
0313         setData(QStringLiteral("Sleep States"), QStringLiteral("Suspend"), m_session->canSuspend());
0314         setData(QStringLiteral("Sleep States"), QStringLiteral("Hibernate"), m_session->canHibernate());
0315         setData(QStringLiteral("Sleep States"), QStringLiteral("HybridSuspend"), m_session->canHybridSuspend());
0316         setData(QStringLiteral("Sleep States"), QStringLiteral("LockScreen"), m_session->canLock());
0317         setData(QStringLiteral("Sleep States"), QStringLiteral("Logout"), m_session->canLogout());
0318     } else if (name == QLatin1String("PowerDevil")) {
0319         createAsyncDBusMethodCallAndCallback<int>(this,
0320                                                   SOLID_POWERMANAGEMENT_SERVICE,
0321                                                   QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"),
0322                                                   QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"),
0323                                                   QStringLiteral("brightness"),
0324                                                   std::bind(&PowermanagementEngine::screenBrightnessChanged, this, std::placeholders::_1));
0325 
0326         createAsyncDBusMethodCallAndCallback<int>(this,
0327                                                   SOLID_POWERMANAGEMENT_SERVICE,
0328                                                   QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"),
0329                                                   QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"),
0330                                                   QStringLiteral("brightnessMax"),
0331                                                   std::bind(&PowermanagementEngine::maximumScreenBrightnessChanged, this, std::placeholders::_1));
0332 
0333         createAsyncDBusMethodCallAndCallback<int>(this,
0334                                                   SOLID_POWERMANAGEMENT_SERVICE,
0335                                                   QStringLiteral("/org/kde/Solid/PowerManagement/Actions/KeyboardBrightnessControl"),
0336                                                   QStringLiteral("org.kde.Solid.PowerManagement.Actions.KeyboardBrightnessControl"),
0337                                                   QStringLiteral("keyboardBrightness"),
0338                                                   std::bind(&PowermanagementEngine::keyboardBrightnessChanged, this, std::placeholders::_1));
0339 
0340         createAsyncDBusMethodCallAndCallback<int>(this,
0341                                                   SOLID_POWERMANAGEMENT_SERVICE,
0342                                                   QStringLiteral("/org/kde/Solid/PowerManagement/Actions/KeyboardBrightnessControl"),
0343                                                   QStringLiteral("org.kde.Solid.PowerManagement.Actions.KeyboardBrightnessControl"),
0344                                                   QStringLiteral("keyboardBrightnessMax"),
0345                                                   std::bind(&PowermanagementEngine::maximumKeyboardBrightnessChanged, this, std::placeholders::_1));
0346 
0347         createPowerManagementDBusMethodCallAndNotifyChanged<bool>(QStringLiteral("isLidPresent"), [this](bool replyValue) {
0348             setData(QStringLiteral("PowerDevil"), QStringLiteral("Is Lid Present"), replyValue);
0349         });
0350 
0351         createAsyncDBusMethodCallAndCallback<bool>(this,
0352                                                    SOLID_POWERMANAGEMENT_SERVICE,
0353                                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/HandleButtonEvents"),
0354                                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.HandleButtonEvents"),
0355                                                    QStringLiteral("triggersLidAction"),
0356                                                    std::bind(&PowermanagementEngine::triggersLidActionChanged, this, std::placeholders::_1));
0357     } else if (name == QLatin1String("PowerManagement")) {
0358         createAsyncDBusMethodCallAndCallback<bool>(this,
0359                                                    QStringLiteral("org.freedesktop.PowerManagement"),
0360                                                    QStringLiteral("/org/freedesktop/PowerManagement"),
0361                                                    QStringLiteral("org.freedesktop.PowerManagement.Inhibit"),
0362                                                    QStringLiteral("HasInhibit"),
0363                                                    [this](const bool &replyValue) {
0364                                                        hasInhibitionChanged(replyValue);
0365                                                    });
0366 
0367     } else if (name == QLatin1String("Inhibitions")) {
0368         createAsyncDBusMethodCallAndCallback<QList<InhibitionInfo>>(this,
0369                                                                     SOLID_POWERMANAGEMENT_SERVICE,
0370                                                                     QStringLiteral("/org/kde/Solid/PowerManagement/PolicyAgent"),
0371                                                                     QStringLiteral("org.kde.Solid.PowerManagement.PolicyAgent"),
0372                                                                     QStringLiteral("ListInhibitions"),
0373                                                                     [this](const QList<InhibitionInfo> &replyValue) {
0374                                                                         removeAllData(QStringLiteral("Inhibitions"));
0375                                                                         inhibitionsChanged(replyValue, QStringList());
0376                                                                     });
0377         // any info concerning lock screen/screensaver goes here
0378     } else if (name == QLatin1String("UserActivity")) {
0379         setData(QStringLiteral("UserActivity"), QStringLiteral("IdleTime"), KIdleTime::instance()->idleTime());
0380     } else if (name == QLatin1String("Power Profiles")) {
0381         updatePowerProfileDaemonInstalled(QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("net.hadess.PowerProfiles")).value());
0382         createPowerProfileDBusMethodCallAndNotifyChanged<QString>(
0383             QStringLiteral("currentProfile"),
0384             std::bind(&PowermanagementEngine::updatePowerProfileCurrentProfile, this, std::placeholders::_1));
0385         createPowerProfileDBusMethodCallAndNotifyChanged<QStringList>(
0386             QStringLiteral("profileChoices"),
0387             std::bind(&PowermanagementEngine::updatePowerProfileChoices, this, std::placeholders::_1));
0388         createPowerProfileDBusMethodCallAndNotifyChanged<QString>(
0389             QStringLiteral("performanceInhibitedReason"),
0390             std::bind(&PowermanagementEngine::updatePowerProfilePerformanceInhibitedReason, this, std::placeholders::_1));
0391         createPowerProfileDBusMethodCallAndNotifyChanged<QString>(
0392             QStringLiteral("performanceDegradedReason"),
0393             std::bind(&PowermanagementEngine::updatePowerProfilePerformanceDegradedReason, this, std::placeholders::_1));
0394         createPowerProfileDBusMethodCallAndNotifyChanged<QList<QVariantMap>>(
0395             QStringLiteral("profileHolds"),
0396             std::bind(&PowermanagementEngine::updatePowerProfileHolds, this, std::placeholders::_1));
0397     } else {
0398         qDebug() << "Data for" << name << "not found";
0399         return false;
0400     }
0401     return true;
0402 }
0403 
0404 QString PowermanagementEngine::batteryTypeToString(const Solid::Battery *battery) const
0405 {
0406     switch (battery->type()) {
0407     case Solid::Battery::PrimaryBattery:
0408         return QStringLiteral("Battery");
0409     case Solid::Battery::UpsBattery:
0410         return QStringLiteral("Ups");
0411     case Solid::Battery::MonitorBattery:
0412         return QStringLiteral("Monitor");
0413     case Solid::Battery::MouseBattery:
0414         return QStringLiteral("Mouse");
0415     case Solid::Battery::KeyboardBattery:
0416         return QStringLiteral("Keyboard");
0417     case Solid::Battery::PdaBattery:
0418         return QStringLiteral("Pda");
0419     case Solid::Battery::PhoneBattery:
0420         return QStringLiteral("Phone");
0421     case Solid::Battery::GamingInputBattery:
0422         return QStringLiteral("GamingInput");
0423     case Solid::Battery::BluetoothBattery:
0424         return QStringLiteral("Bluetooth");
0425     default:
0426         return QStringLiteral("Unknown");
0427     }
0428 }
0429 
0430 bool PowermanagementEngine::updateSourceEvent(const QString &source)
0431 {
0432     if (source == QLatin1String("UserActivity")) {
0433         setData(QStringLiteral("UserActivity"), QStringLiteral("IdleTime"), KIdleTime::instance()->idleTime());
0434         return true;
0435     }
0436     return Plasma5Support::DataEngine::updateSourceEvent(source);
0437 }
0438 
0439 Plasma5Support::Service *PowermanagementEngine::serviceForSource(const QString &source)
0440 {
0441     if (source == QLatin1String("PowerDevil")) {
0442         return new PowerManagementService(this);
0443     }
0444 
0445     return nullptr;
0446 }
0447 
0448 QString PowermanagementEngine::batteryStateToString(int newState) const
0449 {
0450     QString state(QStringLiteral("Unknown"));
0451     if (newState == Solid::Battery::NoCharge) {
0452         state = QLatin1String("NoCharge");
0453     } else if (newState == Solid::Battery::Charging) {
0454         state = QLatin1String("Charging");
0455     } else if (newState == Solid::Battery::Discharging) {
0456         state = QLatin1String("Discharging");
0457     } else if (newState == Solid::Battery::FullyCharged) {
0458         state = QLatin1String("FullyCharged");
0459     }
0460 
0461     return state;
0462 }
0463 
0464 void PowermanagementEngine::updateBatteryChargeState(int newState, const QString &udi)
0465 {
0466     const QString source = m_batterySources[udi];
0467     setData(source, QStringLiteral("State"), batteryStateToString(newState));
0468     updateOverallBattery();
0469 }
0470 
0471 void PowermanagementEngine::updateBatteryPresentState(bool newState, const QString &udi)
0472 {
0473     const QString source = m_batterySources[udi];
0474     setData(source, QStringLiteral("Plugged in"), newState); // FIXME This needs to be renamed and Battery Monitor adjusted
0475 }
0476 
0477 void PowermanagementEngine::updateBatteryChargePercent(int newValue, const QString &udi)
0478 {
0479     const QString source = m_batterySources[udi];
0480     setData(source, QStringLiteral("Percent"), newValue);
0481     updateOverallBattery();
0482 }
0483 
0484 void PowermanagementEngine::updateBatteryEnergy(double newValue, const QString &udi)
0485 {
0486     const QString source = m_batterySources[udi];
0487     setData(source, QStringLiteral("Energy"), newValue);
0488 }
0489 
0490 void PowermanagementEngine::updateBatteryPowerSupplyState(bool newState, const QString &udi)
0491 {
0492     const QString source = m_batterySources[udi];
0493     setData(source, QStringLiteral("Is Power Supply"), newState);
0494 }
0495 
0496 void PowermanagementEngine::updateBatteryNames()
0497 {
0498     uint unnamedBatteries = 0;
0499     for (const QString &source : std::as_const(m_batterySources)) {
0500         DataContainer *batteryDataContainer = containerForSource(source);
0501         if (batteryDataContainer) {
0502             const QString batteryVendor = batteryDataContainer->data()[QStringLiteral("Vendor")].toString();
0503             const QString batteryProduct = batteryDataContainer->data()[QStringLiteral("Product")].toString();
0504 
0505             // Don't show battery name for primary power supply batteries. They usually have cryptic serial number names.
0506             const bool showBatteryName = batteryDataContainer->data()[QStringLiteral("Type")].toString() != QLatin1String("Battery")
0507                 || !batteryDataContainer->data()[QStringLiteral("Is Power Supply")].toBool();
0508 
0509             if (!batteryProduct.isEmpty() && batteryProduct != QLatin1String("Unknown Battery") && showBatteryName) {
0510                 if (!batteryVendor.isEmpty()) {
0511                     setData(source, QStringLiteral("Pretty Name"), QString(batteryVendor + ' ' + batteryProduct));
0512                 } else {
0513                     setData(source, QStringLiteral("Pretty Name"), batteryProduct);
0514                 }
0515             } else {
0516                 ++unnamedBatteries;
0517                 if (unnamedBatteries > 1) {
0518                     setData(source, QStringLiteral("Pretty Name"), i18nc("Placeholder is the battery number", "Battery %1", unnamedBatteries));
0519                 } else {
0520                     setData(source, QStringLiteral("Pretty Name"), i18n("Battery"));
0521                 }
0522             }
0523         }
0524     }
0525 }
0526 
0527 void PowermanagementEngine::updateOverallBattery()
0528 {
0529     const QList<Solid::Device> listBattery = Solid::Device::listFromType(Solid::DeviceInterface::Battery);
0530     bool hasCumulative = false;
0531 
0532     double energy = 0;
0533     double totalEnergy = 0;
0534     bool allFullyCharged = true;
0535     bool charging = false;
0536     bool noCharge = false;
0537     double totalPercentage = 0;
0538     int count = 0;
0539 
0540     for (const Solid::Device &deviceBattery : listBattery) {
0541         const Solid::Battery *battery = deviceBattery.as<Solid::Battery>();
0542 
0543         if (battery && battery->isPowerSupply()) {
0544             hasCumulative = true;
0545 
0546             energy += battery->energy();
0547             totalEnergy += battery->energyFull();
0548             totalPercentage += battery->chargePercent();
0549             allFullyCharged = allFullyCharged && (battery->chargeState() == Solid::Battery::FullyCharged);
0550             charging = charging || (battery->chargeState() == Solid::Battery::Charging);
0551             noCharge = noCharge || (battery->chargeState() == Solid::Battery::NoCharge);
0552             ++count;
0553         }
0554     }
0555 
0556     if (count == 1) {
0557         // Energy is sometimes way off causing us to show rubbish; this is a UPower issue
0558         // but anyway having just one battery and the tooltip showing strange readings
0559         // compared to the popup doesn't look polished.
0560         setData(QStringLiteral("Battery"), QStringLiteral("Percent"), qRound(totalPercentage));
0561     } else if (totalEnergy > 0) {
0562         setData(QStringLiteral("Battery"), QStringLiteral("Percent"), qRound(energy / totalEnergy * 100));
0563     } else if (count > 0) { // UPS don't have energy, see Bug 348588
0564         setData(QStringLiteral("Battery"), QStringLiteral("Percent"), qRound(totalPercentage / static_cast<qreal>(count)));
0565     } else {
0566         setData(QStringLiteral("Battery"), QStringLiteral("Percent"), int(0));
0567     }
0568 
0569     if (hasCumulative) {
0570         if (allFullyCharged) {
0571             setData(QStringLiteral("Battery"), QStringLiteral("State"), "FullyCharged");
0572         } else if (charging) {
0573             setData(QStringLiteral("Battery"), QStringLiteral("State"), "Charging");
0574         } else if (noCharge) {
0575             setData(QStringLiteral("Battery"), QStringLiteral("State"), "NoCharge");
0576         } else {
0577             setData(QStringLiteral("Battery"), QStringLiteral("State"), "Discharging");
0578         }
0579     } else {
0580         setData(QStringLiteral("Battery"), QStringLiteral("State"), "Unknown");
0581     }
0582 
0583     setData(QStringLiteral("Battery"), QStringLiteral("Has Cumulative"), hasCumulative);
0584 }
0585 
0586 void PowermanagementEngine::updateAcPlugState(bool onBattery)
0587 {
0588     setData(QStringLiteral("AC Adapter"), QStringLiteral("Plugged in"), !onBattery);
0589 }
0590 
0591 void PowermanagementEngine::updatePowerProfileDaemonInstalled(const bool &installed)
0592 {
0593     setData(QStringLiteral("Power Profiles"), QStringLiteral("Installed"), installed);
0594 }
0595 
0596 void PowermanagementEngine::updatePowerProfileCurrentProfile(const QString &activeProfile)
0597 {
0598     setData(QStringLiteral("Power Profiles"), QStringLiteral("Current Profile"), activeProfile);
0599 }
0600 
0601 void PowermanagementEngine::updatePowerProfileChoices(const QStringList &choices)
0602 {
0603     setData(QStringLiteral("Power Profiles"), QStringLiteral("Profiles"), choices);
0604 }
0605 
0606 void PowermanagementEngine::updatePowerProfilePerformanceInhibitedReason(const QString &reason)
0607 {
0608     setData(QStringLiteral("Power Profiles"), QStringLiteral("Performance Inhibited Reason"), reason);
0609 }
0610 
0611 void PowermanagementEngine::updatePowerProfilePerformanceDegradedReason(const QString &reason)
0612 {
0613     setData(QStringLiteral("Power Profiles"), QStringLiteral("Performance Degraded Reason"), reason);
0614 }
0615 
0616 void PowermanagementEngine::updatePowerProfileHolds(const QList<QVariantMap> &holds)
0617 {
0618     QList<QVariantMap> out;
0619     std::transform(holds.cbegin(), holds.cend(), std::back_inserter(out), [this](const QVariantMap &hold) {
0620         QString prettyName;
0621         QString icon;
0622         populateApplicationData(hold[QStringLiteral("ApplicationId")].toString(), &prettyName, &icon);
0623         return QVariantMap{
0624             {QStringLiteral("Name"), prettyName},
0625             {QStringLiteral("Icon"), icon},
0626             {QStringLiteral("Reason"), hold[QStringLiteral("Reason")]},
0627             {QStringLiteral("Profile"), hold[QStringLiteral("Profile")]},
0628         };
0629     });
0630     setData(QStringLiteral("Power Profiles"), QStringLiteral("Profile Holds"), QVariant::fromValue(out));
0631 }
0632 
0633 void PowermanagementEngine::deviceRemoved(const QString &udi)
0634 {
0635     if (m_batterySources.contains(udi)) {
0636         Solid::Device device(udi);
0637         Solid::Battery *battery = device.as<Solid::Battery>();
0638         if (battery) {
0639             battery->disconnect(this);
0640         }
0641 
0642         const QString source = m_batterySources[udi];
0643         m_batterySources.remove(udi);
0644         removeSource(source);
0645 
0646         QStringList sourceNames(m_batterySources.values());
0647         sourceNames.removeAll(source);
0648         setData(QStringLiteral("Battery"), QStringLiteral("Sources"), sourceNames);
0649         setData(QStringLiteral("Battery"), QStringLiteral("Has Battery"), !sourceNames.isEmpty());
0650 
0651         updateOverallBattery();
0652     }
0653 }
0654 
0655 void PowermanagementEngine::deviceAdded(const QString &udi)
0656 {
0657     Solid::Device device(udi);
0658     if (device.isValid()) {
0659         const Solid::Battery *battery = device.as<Solid::Battery>();
0660 
0661         if (battery) {
0662             int index = 0;
0663             QStringList sourceNames(m_batterySources.values());
0664             while (sourceNames.contains(QStringLiteral("Battery%1").arg(index))) {
0665                 index++;
0666             }
0667 
0668             const QString source = QStringLiteral("Battery%1").arg(index);
0669             sourceNames << source;
0670             m_batterySources[device.udi()] = source;
0671 
0672             connect(battery, &Solid::Battery::chargeStateChanged, this, &PowermanagementEngine::updateBatteryChargeState);
0673             connect(battery, &Solid::Battery::chargePercentChanged, this, &PowermanagementEngine::updateBatteryChargePercent);
0674             connect(battery, &Solid::Battery::energyChanged, this, &PowermanagementEngine::updateBatteryEnergy);
0675             connect(battery, &Solid::Battery::presentStateChanged, this, &PowermanagementEngine::updateBatteryPresentState);
0676             connect(battery, &Solid::Battery::powerSupplyStateChanged, this, &PowermanagementEngine::updateBatteryPowerSupplyState);
0677 
0678             // Set initial values
0679             updateBatteryChargeState(battery->chargeState(), device.udi());
0680             updateBatteryChargePercent(battery->chargePercent(), device.udi());
0681             updateBatteryEnergy(battery->energy(), device.udi());
0682             updateBatteryPresentState(battery->isPresent(), device.udi());
0683             updateBatteryPowerSupplyState(battery->isPowerSupply(), device.udi());
0684 
0685             setData(source, QStringLiteral("Vendor"), device.vendor());
0686             setData(source, QStringLiteral("Product"), device.product());
0687             setData(source, QStringLiteral("Capacity"), battery->capacity());
0688             setData(source, QStringLiteral("Type"), batteryTypeToString(battery));
0689 
0690             setData(QStringLiteral("Battery"), QStringLiteral("Sources"), sourceNames);
0691             setData(QStringLiteral("Battery"), QStringLiteral("Has Battery"), !sourceNames.isEmpty());
0692 
0693             updateBatteryNames();
0694             updateOverallBattery();
0695         }
0696     }
0697 }
0698 
0699 void PowermanagementEngine::batteryRemainingTimeChanged(qulonglong time)
0700 {
0701     // qDebug() << "Remaining time 2:" << time;
0702     setData(QStringLiteral("Battery"), QStringLiteral("Remaining msec"), time);
0703 }
0704 
0705 void PowermanagementEngine::smoothedBatteryRemainingTimeChanged(qulonglong time)
0706 {
0707     setData(QStringLiteral("Battery"), QStringLiteral("Smoothed Remaining msec"), time);
0708 }
0709 
0710 void PowermanagementEngine::screenBrightnessChanged(int brightness)
0711 {
0712     setData(QStringLiteral("PowerDevil"), QStringLiteral("Screen Brightness"), brightness);
0713 }
0714 
0715 void PowermanagementEngine::maximumScreenBrightnessChanged(int maximumBrightness)
0716 {
0717     setData(QStringLiteral("PowerDevil"), QStringLiteral("Maximum Screen Brightness"), maximumBrightness);
0718     setData(QStringLiteral("PowerDevil"), QStringLiteral("Screen Brightness Available"), maximumBrightness > 0);
0719 }
0720 
0721 void PowermanagementEngine::keyboardBrightnessChanged(int brightness)
0722 {
0723     setData(QStringLiteral("PowerDevil"), QStringLiteral("Keyboard Brightness"), brightness);
0724 }
0725 
0726 void PowermanagementEngine::maximumKeyboardBrightnessChanged(int maximumBrightness)
0727 {
0728     setData(QStringLiteral("PowerDevil"), QStringLiteral("Maximum Keyboard Brightness"), maximumBrightness);
0729     setData(QStringLiteral("PowerDevil"), QStringLiteral("Keyboard Brightness Available"), maximumBrightness > 0);
0730 }
0731 
0732 void PowermanagementEngine::triggersLidActionChanged(bool triggers)
0733 {
0734     setData(QStringLiteral("PowerDevil"), QStringLiteral("Triggers Lid Action"), triggers);
0735 }
0736 
0737 void PowermanagementEngine::hasInhibitionChanged(bool inhibited)
0738 {
0739     setData(QStringLiteral("PowerManagement"), QStringLiteral("Has Inhibition"), inhibited);
0740 }
0741 
0742 void PowermanagementEngine::inhibitionsChanged(const QList<InhibitionInfo> &added, const QStringList &removed)
0743 {
0744     for (auto it = removed.constBegin(); it != removed.constEnd(); ++it) {
0745         removeData(QStringLiteral("Inhibitions"), (*it));
0746     }
0747 
0748     for (auto it = added.constBegin(); it != added.constEnd(); ++it) {
0749         const QString &name = (*it).first;
0750         QString prettyName;
0751         QString icon;
0752         const QString &reason = (*it).second;
0753 
0754         populateApplicationData(name, &prettyName, &icon);
0755 
0756         setData(QStringLiteral("Inhibitions"),
0757                 name,
0758                 QVariantMap{{QStringLiteral("Name"), prettyName}, {QStringLiteral("Icon"), icon}, {QStringLiteral("Reason"), reason}});
0759     }
0760 }
0761 
0762 template<typename ReplyType>
0763 inline void PowermanagementEngine::createPowerManagementDBusMethodCallAndNotifyChanged(const QString &method, std::function<void(ReplyType)> &&callback)
0764 {
0765     createAsyncDBusMethodCallAndCallback<ReplyType>(this,
0766                                                     SOLID_POWERMANAGEMENT_SERVICE,
0767                                                     QStringLiteral("/org/kde/Solid/PowerManagement"),
0768                                                     SOLID_POWERMANAGEMENT_SERVICE,
0769                                                     method,
0770                                                     std::move(callback));
0771 }
0772 
0773 template<typename ReplyType>
0774 inline void PowermanagementEngine::createPowerProfileDBusMethodCallAndNotifyChanged(const QString &method, std::function<void(ReplyType)> &&callback)
0775 {
0776     createAsyncDBusMethodCallAndCallback<ReplyType>(this,
0777                                                     SOLID_POWERMANAGEMENT_SERVICE,
0778                                                     QStringLiteral("/org/kde/Solid/PowerManagement/Actions/PowerProfile"),
0779                                                     QStringLiteral("org.kde.Solid.PowerManagement.Actions.PowerProfile"),
0780                                                     method,
0781                                                     std::move(callback));
0782 }
0783 
0784 void PowermanagementEngine::populateApplicationData(const QString &name, QString *prettyName, QString *icon)
0785 {
0786     if (m_applicationInfo.contains(name)) {
0787         const auto &info = m_applicationInfo.value(name);
0788         *prettyName = info.first;
0789         *icon = info.second;
0790     } else {
0791         KService::Ptr service = KService::serviceByStorageId(name + ".desktop");
0792         if (service) {
0793             *prettyName = service->name(); // cannot be null
0794             *icon = service->icon();
0795 
0796             m_applicationInfo.insert(name, qMakePair(*prettyName, *icon));
0797         } else {
0798             *prettyName = name;
0799             *icon = name.section(QLatin1Char('/'), -1).toLower();
0800             if (!QIcon::hasThemeIcon(*icon)) {
0801                 icon->clear();
0802             }
0803         }
0804     }
0805 }
0806 
0807 void PowermanagementEngine::chargeStopThresholdChanged(int threshold)
0808 {
0809     setData(QStringLiteral("Battery"), QStringLiteral("Charge Stop Threshold"), threshold);
0810 }
0811 
0812 K_PLUGIN_CLASS_WITH_JSON(PowermanagementEngine, "plasma-dataengine-powermanagement.json")
0813 
0814 #include "powermanagementengine.moc"