398 lines
11 KiB
C++
Executable File
398 lines
11 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Gui Plugin for Battery Monitor
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
# Created by Mike Wille, NLE, 2019-01-21
|
|
*/
|
|
|
|
#include "bat_gui_plugin.h"
|
|
|
|
#include <LtsNotification/lts_notification.h>
|
|
#include <LtsSession/lts_session.h>
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <LtsWidgets/lts_base_layout_widget.h>
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include <Windows.h> // windows battery api
|
|
#include <wlanapi.h>
|
|
#endif
|
|
#include <QProcess>
|
|
#include <math.h>
|
|
|
|
|
|
namespace Battery
|
|
{
|
|
|
|
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
|
{
|
|
return std::make_shared<InterfaceImpl>(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<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
|
{
|
|
QVector<LtsWidgets::PluginData> 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() <<std::endl;
|
|
|
|
if (line.startsWith(QStringLiteral("state")))
|
|
{
|
|
QStringList parts = line.split(QLatin1Char(':'));
|
|
if (parts.size() > 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<const char*>(&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
|