File indexing completed on 2024-04-21 04:56:49

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "batteryplugin.h"
0008 
0009 #include <KLocalizedString>
0010 #include <KPluginFactory>
0011 
0012 #include <Solid/Battery>
0013 #include <Solid/Device>
0014 #include <Solid/Predicate>
0015 
0016 #include <core/daemon.h>
0017 
0018 #include "plugin_battery_debug.h"
0019 
0020 K_PLUGIN_CLASS_WITH_JSON(BatteryPlugin, "kdeconnect_battery.json")
0021 
0022 const auto batteryDevice = Solid::DeviceInterface::Type::Battery;
0023 const auto primary = Solid::Battery::BatteryType::PrimaryBattery;
0024 
0025 BatteryPlugin::BatteryPlugin(QObject *parent, const QVariantList &args)
0026     : KdeConnectPlugin(parent, args)
0027 {
0028     QList<Solid::Device> batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary));
0029 
0030     if (batteries.isEmpty()) {
0031         qCWarning(KDECONNECT_PLUGIN_BATTERY) << "No Primary Battery detected on this system. This may be a bug.";
0032         QList<Solid::Device> allBatteries = Solid::Device::listFromType(batteryDevice);
0033         qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Total quantity of batteries found: " << allBatteries.size();
0034         return;
0035     }
0036 
0037     // Ok, there's at least one. Let's assume it will remain attached (for most laptops
0038     // and desktops, this is a safe assumption).
0039     const Solid::Battery *chosen = batteries.first().as<Solid::Battery>();
0040 
0041     connect(chosen, &Solid::Battery::chargeStateChanged, this, &BatteryPlugin::slotChargeChanged);
0042     connect(chosen, &Solid::Battery::chargePercentChanged, this, &BatteryPlugin::slotChargeChanged);
0043 }
0044 
0045 int BatteryPlugin::charge() const
0046 {
0047     return m_charge;
0048 }
0049 
0050 bool BatteryPlugin::isCharging() const
0051 {
0052     return m_isCharging;
0053 }
0054 
0055 void BatteryPlugin::connected()
0056 {
0057     // Explicitly send the current charge
0058     slotChargeChanged();
0059 }
0060 
0061 void BatteryPlugin::slotChargeChanged()
0062 {
0063     // Note: the NetworkPacket sent at the end of this method can reflect MULTIPLE batteries.
0064     // We average the total charge against the total number of batteries, which in practice
0065     // seems to work out ok.
0066     bool isAnyBatteryCharging = false;
0067     int batteryQuantity = 0;
0068     int cumulativeCharge = 0;
0069 
0070     QList<Solid::Device> batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary));
0071 
0072     for (auto device : batteries) {
0073         const Solid::Battery *battery = device.as<Solid::Battery>();
0074 
0075         // Don't look at batteries that can be easily detached
0076         if (battery->isPowerSupply()) {
0077             batteryQuantity++;
0078             cumulativeCharge += battery->chargePercent();
0079             if (battery->chargeState() == Solid::Battery::ChargeState::Charging) {
0080                 isAnyBatteryCharging = true;
0081             }
0082         }
0083     }
0084 
0085     if (batteryQuantity == 0) {
0086         qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Primary Battery seems to have been removed. Suspending packets until it is reconnected.";
0087         return;
0088     }
0089 
0090     // Load a new Battery object to represent the first device in the list
0091     Solid::Battery *chosen = batteries.first().as<Solid::Battery>();
0092 
0093     // Prepare an outgoing network packet
0094     NetworkPacket status(PACKET_TYPE_BATTERY, {{}});
0095     status.set(QStringLiteral("isCharging"), isAnyBatteryCharging);
0096     const int charge = cumulativeCharge / batteryQuantity;
0097     status.set(QStringLiteral("currentCharge"), charge);
0098     // FIXME: In future, we should consider sending an array of battery objects
0099     status.set(QStringLiteral("batteryQuantity"), batteryQuantity);
0100     // We consider the primary battery to be low if it's below 15%
0101     if (charge <= 15 && chosen->chargeState() == Solid::Battery::ChargeState::Discharging) {
0102         status.set(QStringLiteral("thresholdEvent"), (int)ThresholdBatteryLow);
0103     } else {
0104         status.set(QStringLiteral("thresholdEvent"), (int)ThresholdNone);
0105     }
0106     sendPacket(status);
0107 }
0108 
0109 void BatteryPlugin::receivePacket(const NetworkPacket &np)
0110 {
0111     m_isCharging = np.get<bool>(QStringLiteral("isCharging"), false);
0112     m_charge = np.get<int>(QStringLiteral("currentCharge"), -1);
0113     const int thresholdEvent = np.get<int>(QStringLiteral("thresholdEvent"), (int)ThresholdNone);
0114 
0115     Q_EMIT refreshed(m_isCharging, m_charge);
0116 
0117     if (thresholdEvent == ThresholdBatteryLow && !m_isCharging) {
0118         Daemon::instance()->sendSimpleNotification(QStringLiteral("batteryLow"),
0119                                                    i18nc("device name: low battery", "%1: Low Battery", device()->name()),
0120                                                    i18n("Battery at %1%", m_charge),
0121                                                    QStringLiteral("battery-040"));
0122     }
0123 }
0124 
0125 QString BatteryPlugin::dbusPath() const
0126 {
0127     return QLatin1String("/modules/kdeconnect/devices/%1/battery").arg(device()->id());
0128 }
0129 
0130 #include "batteryplugin.moc"
0131 #include "moc_batteryplugin.cpp"