Skip to content

Commit

Permalink
added phone battery indicator
Browse files Browse the repository at this point in the history
  • Loading branch information
WebFreak001 committed Mar 17, 2019
1 parent 71f7af4 commit 8b747b2
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 4 deletions.
Binary file added res/icon/cellphone-erase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions source/app.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import dwinbar.widgets.battery;
import dwinbar.widgets.phone_battery;
import dwinbar.widgets.clock;
import dwinbar.widgets.mediaplayer;
import dwinbar.widgets.notifications;
import dwinbar.widgets.volume;
import dwinbar.widgets.workspace;

import dwinbar.kdeconnect;

/*import dwinbar.widgets.volume;
import dwinbar.widgets.tray;
import dwinbar.widgets.workspace;*/
Expand All @@ -25,6 +28,8 @@ void main(string[] args)
string left = args.length > 1 ? args[1] : null;
string right = args.length > 2 ? args[2] : null;

auto phones = KDEConnectDevice.listDevices();

//dfmt off
auto panel1 = bar.addPanel(Screen.First, Dock.Bottom, panelConfig)
.add(new ClockWidget())
Expand All @@ -35,6 +40,9 @@ void main(string[] args)
.add(new MprisMediaPlayerWidget(bar.fontFamily, "org.mpris.MediaPlayer2.spotify"))
//.add(new WorkspaceWidget(bar.x, "DisplayPort-1"))
;
if (phones.length)
panel1.add(new PhoneBatteryWidget(bar.fontFamily, phones[0]));

if (left.length)
panel1.add(new WorkspaceWidget(bar.x, left));

Expand Down
95 changes: 95 additions & 0 deletions source/dwinbar/kdeconnect.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
module dwinbar.kdeconnect;

import dwinbar.backend.dbus;

import std.stdio;

import std.xml;

struct KDEConnectDevice
{
string id;
PathIface properties;
PathIface device;
PathIface battery;

bool chargingCache;
int batteryCache;

bool isReachable()
{
try
{
return properties.Get("org.kde.kdeconnect.device", "isReachable").to!DBusAny
.get!bool;
}
catch (Exception e)
{
return false;
}
}

bool isCharging()
{
try
{
return chargingCache = battery.isCharging().to!bool;
}
catch (Exception)
{
return chargingCache;
}
}

int charge() @property
{
try
{
return batteryCache = battery.charge().to!int;
}
catch (Exception)
{
return batteryCache;
}
}

static KDEConnectDevice[] listDevices()
{
try
{
sessionBus.attach();
auto conn = new PathIface(sessionBus.conn, "org.kde.kdeconnect",
"/modules/kdeconnect/devices", "org.freedesktop.DBus.Introspectable");

KDEConnectDevice[] ret;

auto xml = new DocumentParser(conn.Introspect().to!string);
xml.onStartTag["node"] = (ElementParser xml) {
string id = xml.tag.attr["name"];
try
{
auto path = "/modules/kdeconnect/devices/" ~ id;
PathIface properties = new PathIface(sessionBus.conn,
"org.kde.kdeconnect", path, "org.freedesktop.DBus.Properties");
PathIface device = new PathIface(sessionBus.conn,
"org.kde.kdeconnect", path, "org.kde.kdeconnect.device");
PathIface battery = new PathIface(sessionBus.conn,
"org.kde.kdeconnect", path, "org.kde.kdeconnect.device.battery");
ret ~= KDEConnectDevice(id, properties, device, battery);
}
catch (Exception e)
{
stderr.writeln("Failed to attach to device " ~ id ~ ": " ~ e.msg);
}
};
xml.parse();

return ret;
}
catch (Exception e)
{
stderr.writeln(e);
return null;
}
}
}
16 changes: 12 additions & 4 deletions source/dwinbar/widgets/battery.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import tinyevent;

class BatteryWidget : Widget
{
this(FontFamily font)
{
this.font = font;
}

this(FontFamily font, string batteryDevice)
{
this.font = font;
Expand All @@ -32,12 +37,13 @@ class BatteryWidget : Widget
chargingIcon.loadAll("res/icon/battery-charging-");
if (!batteryIcon.images.length)
throw new Exception("No battery icons found");
unknownIcon = batteryIcon.imageFor(0);
updateClock.start();
}

override int width(bool) const
{
return 17 + cast(int) ceil(measureText(cast() font, 1, batteryLevel.to!string)[0]);
return 18 + cast(int) ceil(measureText(cast() font, 1, batteryLevel.to!string)[0]);
}

override int height(bool) const
Expand Down Expand Up @@ -68,9 +74,11 @@ class BatteryWidget : Widget
icon = batteryFullIcon;
break;
case BatteryState.empty:
case BatteryState.unknown:
icon = batteryIcon.imageFor(0);
break;
case BatteryState.unknown:
icon = unknownIcon;
break;
default:
icon = batteryIcon.imageFor(batteryLevel);
break;
Expand Down Expand Up @@ -111,13 +119,13 @@ class BatteryWidget : Widget
}
}

private:
protected:
FontFamily font;
PathIface batteryInterface;
BatteryState batteryState;
int batteryLevel;
int animatedBatteryLevel;
IFImage batteryFullIcon;
IFImage batteryFullIcon, unknownIcon;
ImageRange batteryIcon, chargingIcon;
StopWatch updateClock;
}
Expand Down
109 changes: 109 additions & 0 deletions source/dwinbar/widgets/phone_battery.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
module dwinbar.widgets.phone_battery;

import dwinbar.backend.dbus;
import dwinbar.widgets.battery;
import dwinbar.widget;
import dwinbar.kdeconnect;
import dwinbar.bar;

import std.algorithm;
import std.array;
import std.conv;
import std.datetime.stopwatch;
import std.exception;
import std.file;
import std.math;
import std.path;
import std.range;
import std.stdio;
import std.typecons;
import std.uni;

import tinyevent;

class PhoneBatteryWidget : BatteryWidget
{
this(FontFamily font, KDEConnectDevice device)
{
super(font);
this.device = device;
unknownIcon = read_image("res/icon/cellphone-erase.png").premultiply;
batteryFullIcon = read_image("res/icon/battery-charging-full.png").premultiply;
batteryIcon.loadAll("res/icon/battery-");
chargingIcon.loadAll("res/icon/battery-charging-");
if (!batteryIcon.images.length)
throw new Exception("No battery icons found");
updateClock.start();
}

override void update(Bar bar)
{
if (updateClock.peek <= 400.msecs)
return;
updateClock.reset();
if (tick++ >= 25)
{
tick = 0;
updateDBus();
BatteryState newState = BatteryState.unknown;
int newBatteryLevel = device.charge;
if (device.isReachable)
newState = device.isCharging ? (newBatteryLevel == 100
? BatteryState.fullyCharged : BatteryState.charging) : BatteryState.discharging;
if (newBatteryLevel != batteryLevel)
{
batteryLevel = newBatteryLevel;
queueRedraw();
}
if (newState != batteryState)
{
batteryState = newState;
animatedBatteryLevel = batteryLevel;
queueRedraw();
}
}
if (batteryState == BatteryState.charging)
{
animatedBatteryLevel += 5;
if (animatedBatteryLevel > 105)
animatedBatteryLevel = batteryLevel;
queueRedraw();
}
}

override IFImage redraw(bool vertical, Bar bar, bool hovered)
{
//dfmt off
enum ulong phone =
(0b00000000UL << 56) |
(0b00011110UL << 48) |
(0b00110011UL << 40) |
(0b00111111UL << 32) |
(0b00100001UL << 24) |
(0b00100001UL << 16) |
(0b00100001UL << 8) |
(0b00011110UL)
;
//dfmt on

auto ret = super.redraw(vertical, bar, hovered);
if (batteryState == BatteryState.unknown)
return ret;

assert(ret.c == 4);
foreach (i; 0 .. 62)
{
int x = i & 0b111;
int y = i >> 3;
if (x == 7)
continue;
ret.pixels[(x + y * ret.w) * 4 .. (x + y * ret.w) * 4 + 4] = (phone & (1UL << i)) != 0UL ? 0xFF
: 0;
}
return ret;
}

private:
int tick = 25;
KDEConnectDevice device;
}

0 comments on commit 8b747b2

Please sign in to comment.