/** * SPDX-FileCopyrightText: 2013 Albert Vaca * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "batteryplugin.h" #include #include #include #include #include #include #include "plugin_battery_debug.h" K_PLUGIN_CLASS_WITH_JSON(BatteryPlugin, "kdeconnect_battery.json") BatteryPlugin::BatteryPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } int BatteryPlugin::charge() const { return m_charge; } bool BatteryPlugin::isCharging() const { return m_isCharging; } void BatteryPlugin::connected() { // We've just connected. Request battery information from the remote device... NetworkPacket np(PACKET_TYPE_BATTERY_REQUEST, {{QStringLiteral("request"),true}}); sendPacket(np); // ...and then figure out whether we have any batteries const auto batteryDevice = Solid::DeviceInterface::Type::Battery; const auto primary = Solid::Battery::BatteryType::PrimaryBattery; QList batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary)); if (batteries.isEmpty()) { qCWarning(KDECONNECT_PLUGIN_BATTERY) << "No Primary Battery detected on this system. This may be a bug."; QList allBatteries = Solid::Device::listFromType(batteryDevice); qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Total quantity of batteries found: " << allBatteries.size(); return; } // Ok, there's at least one. Let's assume it will remain attached (for most laptops // and desktops, this is a safe assumption). const Solid::Battery* chosen = batteries.first().as(); connect(chosen, &Solid::Battery::chargeStateChanged, this, &BatteryPlugin::slotChargeChanged); connect(chosen, &Solid::Battery::chargePercentChanged, this, &BatteryPlugin::slotChargeChanged); // Explicitly send the current charge slotChargeChanged(); } void BatteryPlugin::slotChargeChanged() { // Note: the NetworkPacket sent at the end of this method can reflect MULTIPLE batteries. // We average the total charge against the total number of batteries, which in practice // seems to work out ok. bool isAnyBatteryCharging = false; int batteryQuantity = 0; int cumulativeCharge = 0; const auto batteryDevice = Solid::DeviceInterface::Type::Battery; const auto primary = Solid::Battery::BatteryType::PrimaryBattery; QList batteries = Solid::Device::listFromQuery(Solid::Predicate(batteryDevice, QStringLiteral("type"), primary)); for (auto device : batteries) { const Solid::Battery* battery = device.as(); // Don't look at batteries that can be easily detached if (battery->isPowerSupply()) { batteryQuantity++; cumulativeCharge += battery->chargePercent(); if (battery->chargeState() == Solid::Battery::ChargeState::Charging) { isAnyBatteryCharging = true; } } } if (batteryQuantity == 0) { qCWarning(KDECONNECT_PLUGIN_BATTERY) << "Primary Battery seems to have been removed. Suspending packets until it is reconnected."; return; } // Load a new Battery object to represent the first device in the list Solid::Battery* chosen = batteries.first().as(); // Prepare an outgoing network packet NetworkPacket status(PACKET_TYPE_BATTERY, {{}}); status.set(QStringLiteral("isCharging"), isAnyBatteryCharging); status.set(QStringLiteral("currentCharge"), cumulativeCharge / batteryQuantity); // FIXME: In future, we should consider sending an array of battery objects status.set(QStringLiteral("batteryQuantity"), batteryQuantity); // We consider the primary battery to be low if it won't last another 10 minutes. // This doesn't necessarily work if (for example) Solid finds multiple batteries. // FIXME: In future, we should check system settings instead of hardcoding an // amount of time. if (chosen->timeToEmpty() < 600 && chosen->chargeState() == Solid::Battery::ChargeState::Discharging) { status.set(QStringLiteral("thresholdEvent"), (int)ThresholdBatteryLow); } else { status.set(QStringLiteral("thresholdEvent"), (int)ThresholdNone); } sendPacket(status); } bool BatteryPlugin::receivePacket(const NetworkPacket& np) { m_isCharging = np.get(QStringLiteral("isCharging"), false); m_charge = np.get(QStringLiteral("currentCharge"), -1); const int thresholdEvent = np.get(QStringLiteral("thresholdEvent"), (int)ThresholdNone); Q_EMIT refreshed(m_isCharging, m_charge); if (thresholdEvent == ThresholdBatteryLow && !m_isCharging) { Daemon::instance()->sendSimpleNotification(QStringLiteral("batteryLow"), i18nc("device name: low battery", "%1: Low Battery", device()->name()), i18n("Battery at %1%", m_charge), QStringLiteral("battery-040")); } return true; } QString BatteryPlugin::dbusPath() const { return QStringLiteral("/modules/kdeconnect/devices/") + device()->id() + QStringLiteral("/battery"); } #include "batteryplugin.moc"