/** * @RDE_HEADER@ * Gui Plugin for Battery Monitor * $Id$ * $HeadURL$ * $Author$ # Created by Mike Wille, NLE, 2019-01-21 */ #include "bat_gui_plugin.h" #include #include #include #include #include #ifdef Q_OS_WIN #include // windows battery api #include #endif #include #include namespace Battery { std::shared_ptr GuiPlugin::createGuiInterface(LtsSession::LtsSession *session) { return std::make_shared(session); } InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session) : GuiPluginBase::Interface(session, NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("BatteryGui")) , m_noti(nullptr) , m_notiWlan(nullptr) , m_warnedLow(false) , m_warnedCritical(false) , m_issi(QString()) , m_signal(0) { connect(m_session, &LtsSession::LtsSession::signalDoneInitializingSession, // workaround to only receive the signal once this, &InterfaceImpl::onInitDone); } void InterfaceImpl::onInitDone() { disconnect(m_session, &LtsSession::LtsSession::signalDoneInitializingSession, this, nullptr); // workaround to only receive the signal once checkWlanStatus(); } QVector InterfaceImpl::getActions() { QVector ret; LtsWidgets::PluginData plData; plData.identifier = QLatin1String("CreateBatteryNoti"); plData.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndDiscardAction; plData.directFunc = [this](const LtsWidgets::PluginData &) { m_noti = m_session->createNotification(QStringLiteral("BatteryMonitor")); m_noti->setIcon(QIcon(QStringLiteral("icon:battery-unknown.svg"))); m_noti->setAction([this]() { new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), m_noti->tooltip(), tr("Current battery status")); }); slotCheckBatteryStatus(); QTimer *t = new QTimer(this); t->setInterval(10000); connect(t, &QTimer::timeout, this, &InterfaceImpl::slotCheckBatteryStatus); t->start(); }; ret.append(plData); //Wlan Connection plData.directFunc = [this](const LtsWidgets::PluginData &) { m_notiWlan = m_session->createNotification(QStringLiteral("TabOcWlanConnection")); m_notiWlan->setToolTip(tr("No Wlan Connection")); m_notiWlan->setAction([this]() { LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), m_notiWlan->tooltip()); }); }; ret.append(plData); connect(&m_timer, &QTimer::timeout, this, &InterfaceImpl::checkWlanStatus); m_timer.setInterval(1000); m_timer.start(); return ret; } //#define FAKE_CHARGE #ifdef FAKE_CHARGE static int fakeCharge = 100; #endif bool InterfaceImpl::getBatteryStatus(bool &hasAC, uint8_t &charge, int32_t &lifetime) { #ifdef FAKE_CHARGE // substracts 10% battery charge per status update (5 secs) - turns to 100 again after hitting 0 hasAC = false; charge = fakeCharge; fakeCharge -= 10; if (fakeCharge < 0) fakeCharge = 100; //lifetime = 9000; // 2 hours & 30 minutes (in secs) lifetime = 1800; // 0 hours & 30 minutes (in secs) return true; #else #ifdef Q_OS_WIN SYSTEM_POWER_STATUS status; if (!GetSystemPowerStatus(&status)) { ERROR_LOG(m_logger) << "GetSystemPowerStatus failed"; return false; } hasAC = (status.ACLineStatus == 1); // The classic: Microsoft's Boolean is not boolean charge = status.BatteryLifePercent; lifetime = status.BatteryLifeTime; if (charge == 255) return false; return true; #else // linux // TODO: linux QProcess upower; QString text; QString arg; upower.setProgram(QStringLiteral("upower")); upower.setProcessChannelMode(QProcess::MergedChannels); QStringList args; // first check for BATTERY NAME args << QStringLiteral("-e"); upower.setArguments(args); upower.start(); upower.waitForStarted(); upower.waitForFinished(1000); while( upower.canReadLine()) { QString line = QString::fromUtf8(upower.readLine().trimmed()); if (line.contains(QStringLiteral("BAT"))) arg = line; } if (arg.isEmpty()) return false; args.clear(); args << QStringLiteral("-i"); //args << QStringLiteral("/org/freedesktop/UPower/devices/battery_BAT0"); args << arg ; upower.setArguments(args); //std::cout << "Try Starting upower " << upower.arguments().join(" ").toStdString()<< std::endl; upower.start(); upower.waitForStarted(); upower.waitForFinished(1000); if (!upower.canReadLine()) return false; while( upower.canReadLine()) { const QString &line = QString::fromUtf8(upower.readLine().trimmed()); // std::cout << "line: " << line.toStdString() < 1) { if (parts.at(1).contains(QStringLiteral("discharging"))) hasAC = false; else hasAC = true; } } if (line.contains(QStringLiteral(" to "))) { QStringList parts = line.split(QLatin1Char(':')); if (parts.size() > 1) { text = parts.at(1); text = text.trimmed(); QStringList time = text.split(QLatin1Char(' ')); if (time.size() > 1) { QString timeinX = time.at(0); int p1 = 0; int p2 = 0; QStringList timeSplitt = timeinX.split(QLatin1Char('.')); if (timeSplitt.size() > 1) { p2 = timeSplitt.at(1).toInt(); } p1 = timeSplitt.at(0).toInt(); if (time.at(1).contains(QStringLiteral("minutes"))) { p1 *=60; p2 = (60/10*p2); } else { p1 = p1*60*60; p2 = ((60/10)*p2)*60; } lifetime = p1 + p2; } } } if (line.startsWith(QStringLiteral("percentage"))) { QStringList parts = line.split(QLatin1Char(':')); if (parts.size() > 1) { QString chargeString = parts.at(1); chargeString.chop(1); charge = chargeString.toInt(); } } } //std::cout << "HAS AC: " << hasAC << " percentage: " << charge << " STATE: " << text.toStdString() << " Lifetime: " << lifetime << std::endl; DEBUG_LOG(m_logger) << "HAS AC: " << hasAC << " percentage: " << charge << " STATE: " << text.toStdString() << " Lifetime: " << lifetime << " seconds"; return true; #endif // end linux #endif } void InterfaceImpl::checkWlanStatus() { bool bConnected = false; #ifdef Q_OS_WIN HANDLE hClient; DWORD NegociationVersion; DWORD dwResult = WlanOpenHandle(WLAN_API_VERSION, nullptr, &NegociationVersion, &hClient); if (dwResult == ERROR_SUCCESS) { PWLAN_INTERFACE_INFO_LIST pIfList = nullptr; dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList); if (dwResult == ERROR_SUCCESS) { for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) { const auto &ifInfo = pIfList->InterfaceInfo[i]; PWLAN_AVAILABLE_NETWORK_LIST pBssList = nullptr; dwResult = WlanGetAvailableNetworkList(hClient, &ifInfo.InterfaceGuid, 0, nullptr, &pBssList); if (dwResult == ERROR_SUCCESS) { for (DWORD j = 0; j < pBssList->dwNumberOfItems; j++) { const auto &bssEntry = pBssList->Network[j]; if (bssEntry.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) { bConnected = true; m_issi = QString::fromLocal8Bit(reinterpret_cast(&bssEntry.dot11Ssid.ucSSID[0])); m_signal = bssEntry.wlanSignalQuality; break; } } } WlanFreeMemory(pBssList); if (bConnected) break; } WlanFreeMemory(pIfList); } WlanCloseHandle(hClient, nullptr); } #endif setWlanStatus(bConnected); } void InterfaceImpl::slotCheckBatteryStatus() { if (m_noti == nullptr) return; bool hasAC = false; uint8_t charge = 255; int32_t lifetime = 0; bool success = getBatteryStatus(hasAC, charge, lifetime); QString statusText; QString filename; if (!success) { statusText = tr("unknown"); filename = QStringLiteral("icon:battery-unknown.svg"); } else { int chargeFile = 0; if (charge < 11) // this '11' thing ruins the easy 4-liner I had here first.. chargeFile = 0; else if (charge < 21) chargeFile = 20; else if (charge < 41) chargeFile = 40; else if (charge < 61) chargeFile = 60; else if (charge < 81) chargeFile = 80; else chargeFile = 100; filename = QStringLiteral("icon:battery-%1-%2.svg") .arg(QString::number(chargeFile), hasAC ? QStringLiteral("charging") : QStringLiteral("discharging")); if (hasAC) statusText = tr("Charging"); else statusText = tr("Discharging"); statusText += QStringLiteral(", "); statusText += QString::number(charge); statusText += QStringLiteral("%"); statusText += QStringLiteral(" - %1 hour(s) and %2 minute(s) estimated remaining").arg(lifetime / 60 / 60).arg((lifetime / 60) % 60); } QString tooltip(tr("Battery status: %1").arg(statusText)); m_noti->setIcon(QIcon(filename)); m_noti->setToolTip(tooltip); if (!hasAC) { if (charge < 21 && !m_warnedLow) { m_warnedLow = true; new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("Battery charge is low, please plug the AC adapter in soon")); } else if (charge < 11 && !m_warnedCritical) { m_warnedCritical = true; new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("Battery charge is critical, please plug the AC adapter in now!")); } } else { m_warnedLow = false; m_warnedCritical = false; } } void InterfaceImpl::setWlanStatus(bool isConnected) { if (!m_notiWlan) return; if (isConnected) { m_notiWlan->setToolTip(tr("Connected to Wlan %1 \nSignal strength %2 %").arg(m_issi).arg(m_signal)); if (m_signal > 0) { if (m_signal > 85) m_notiWlan->setIcon(QIcon(QStringLiteral("icon:wireless-green.svg"))); else if (m_signal > 70) m_notiWlan->setIcon(QIcon(QStringLiteral("icon:wireless-yellow.svg"))); else m_notiWlan->setIcon(QIcon(QStringLiteral("icon:wireless-orange.svg"))); } else m_notiWlan->setIcon(QIcon(QStringLiteral("icon:wireless-green.svg"))); } else { m_notiWlan->setToolTip(tr("No Wlan Connection")); m_notiWlan->setIcon(QIcon(QStringLiteral("icon:wireless-red.svg"))); } } } // namespace Battery