Check in initial working version of client and server
This commit is contained in:
commit
01a303b291
3 changed files with 147 additions and 0 deletions
48
README.md
Normal file
48
README.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Kargos remote battery status indicator
|
||||||
|
|
||||||
|
For everyone who uses X2Go to work on a remote workstation from their physical laptop.
|
||||||
|
|
||||||
|
If you have X2Go in fullscreen, it also covers your local battery status indicator and sometimes even battery notifications.
|
||||||
|
|
||||||
|
These scripts will show your laptop's battery status in a Kargos widget on KDE. I would assume that it also works in GNOME Argos as well as macOS BitBar, but I have not tried either of those as remote operating systems.
|
||||||
|
|
||||||
|
I have also only tried Ubuntu as a local operating system (i.e. running directly on the laptop), but other (unixoid) operating systems should work fine if the "upower" application is available.
|
||||||
|
|
||||||
|
Use is not limited to X2Go, of course. Any setup where you can get both machines to access a shared file (e.g. using a manual sshfs or davfs mount) should do the trick.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
NB: "Client" refers to the machine displaying the remote battery status in a Kargos widget (i.e. the remote machine in an X2Go setup), "server" is the device sharing its battery status (i.e. your local laptop).
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
The server application reads the local machine's battery status and writes it to an outfile. Navigate to the directory containing battery2god in your terminal, then execute:
|
||||||
|
|
||||||
|
```./battery2god /path/to/outfile```
|
||||||
|
|
||||||
|
You should use an otherwise empty directory for the outfile and make sure that your user account has permissions to write to that directory. Then set up a shared folder in X2Go (or mount that folder into the remote filesystem in another way), allowing the remote machine to access that directory. X2Go shares automatically have read and write permissions - for other mounts, read-only permissions are sufficient. (You can also mount the remote filesystem into your local filesystem - of course, you need write permissions then.)
|
||||||
|
|
||||||
|
You can easily deamonize the server (i.e. move it to the background so you can use your terminal otherwise) by running this command instead:
|
||||||
|
|
||||||
|
```nohup ./battery2god /path/to/outfile```
|
||||||
|
|
||||||
|
Advanced users can also use cronjobs, systemd services, etc. to make sure the server starts automatically at boot - this is beyond the scope of this readme.
|
||||||
|
|
||||||
|
### Client
|
||||||
|
|
||||||
|
This assumes that you are running an operating system with a KDE Plasma 5 desktop environment. If you use GNOME, you will need to use Argon, if you're on macOS, use BitBar. Note that this is not tested and I will not be including instructions on how to do this. However, you should be able to find those instructions on the Internet rather easily.
|
||||||
|
|
||||||
|
Add a Kargon widget to your KDE environment by right clicking your system tray (where you can see your clock, application widgets, etc. - usually the bottom right corner of your screen in a default setup), clicking "Add Widgets" and dragging a Kargon widget to your system tray from the window that opens. Close that window, then right-click the new widget (should read "No command configured" at that point), then "Configure Kargos". In "Command line or executable path" add the absolute paths to the battery2go client as well as the outfile of the server.
|
||||||
|
|
||||||
|
```/path/to/battery2go /path/to/mountpoint/outfile```
|
||||||
|
|
||||||
|
If you use X2Go shared folders, the mountpoint will be located in /home/USERNAME/media/disk/, use a file browser to find out the exact name of the folder. If you are not sure if you got the command right, you can also just try executing it in a terminal - if you don't get an error message, you are good to go. Press OK, and the widget should display your laptop's battery status.
|
||||||
|
|
||||||
|
## Limitations / Issues
|
||||||
|
|
||||||
|
Writing this readme took longer than writing the client and server scripts - don't expect them to work perfectly in every setup.
|
||||||
|
|
||||||
|
* If your machine has more than one battery, this will not work properly. It will only report the status of one of those batteries.
|
||||||
|
* If you resize your X2Go remote workspace too much, sometimes the widget will stop updating. This is probably an issue with plasmashell itself. Running "plasmashell --replace" should temporarily fix this.
|
||||||
|
|
||||||
|
If you find any further issues or need help getting the scripts running, feel free to open an issue in the Github issue tracker.
|
40
client/battery2go
Executable file
40
client/battery2go
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
|
||||||
|
LOW = 0
|
||||||
|
MEDIUM = 1
|
||||||
|
HIGH = 2
|
||||||
|
|
||||||
|
LOW_THRESHOLD = 20
|
||||||
|
HIGH_THRESHOLD = 80
|
||||||
|
|
||||||
|
def generate_output(percentage: float, charging: bool, remaining: str):
|
||||||
|
if percentage >= 0:
|
||||||
|
status = LOW if percentage < LOW_THRESHOLD else MEDIUM if percentage < HIGH_THRESHOLD else HIGH
|
||||||
|
color = "green" if charging == True else "blue" if status == HIGH else "orange" if status == MEDIUM else "red"
|
||||||
|
icon = "battery-" + ("low" if status == LOW else "good" if status == MEDIUM else "full") + ("-charging" if charging else "")
|
||||||
|
|
||||||
|
print(f"{percentage:.0f}% | color={color} iconName={icon}")
|
||||||
|
print("---")
|
||||||
|
print("Time to %s: %s" % ("full" if charging else "empty", remaining))
|
||||||
|
else:
|
||||||
|
print("Error! | color=red iconName=battery-missing")
|
||||||
|
print("---")
|
||||||
|
print("Data file corrupted or not found")
|
||||||
|
|
||||||
|
def read_values(datafile: os.PathLike):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(datafile)
|
||||||
|
return config.getfloat("BATTERY", "percentage"), config.getboolean("BATTERY", "charging"), config["BATTERY"]["remaining"]
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("datafile")
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
generate_output(*read_values(args.datafile))
|
||||||
|
except:
|
||||||
|
generate_output(-1, False, "")
|
59
server/battery2god
Executable file
59
server/battery2god
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
upath=None
|
||||||
|
|
||||||
|
def read_values(output: str):
|
||||||
|
remaining = False
|
||||||
|
lines = [line.strip() for line in output.split("\n")]
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith("time to"):
|
||||||
|
remaining = line.split(":")[-1].strip()
|
||||||
|
elif line.startswith("percentage"):
|
||||||
|
percentage = float(line.split(":")[-1].strip()[:-1])
|
||||||
|
elif line.startswith("state"):
|
||||||
|
charging = not "discharging" in line
|
||||||
|
return percentage, charging, remaining
|
||||||
|
|
||||||
|
def write_values(percentage: float, charging: bool, remaining: str, datafile: os.PathLike):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config["BATTERY"] = {
|
||||||
|
"percentage": percentage,
|
||||||
|
"charging": charging,
|
||||||
|
"remaining": remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(datafile, "w") as outfile:
|
||||||
|
config.write(outfile)
|
||||||
|
|
||||||
|
def run_upower(path=None):
|
||||||
|
global upath
|
||||||
|
if not (path or upath):
|
||||||
|
finder = subprocess.Popen(["upower", "-e"], stdout=subprocess.PIPE)
|
||||||
|
finder.wait()
|
||||||
|
for line in finder.stdout.read().decode().split("\n"):
|
||||||
|
if "BAT" in line:
|
||||||
|
upath = line
|
||||||
|
break
|
||||||
|
|
||||||
|
path = path or upath
|
||||||
|
|
||||||
|
info = subprocess.Popen(["upower", "-i", path], stdout=subprocess.PIPE)
|
||||||
|
info.wait()
|
||||||
|
return info.stdout.read().decode()
|
||||||
|
|
||||||
|
def run_loop(datafile):
|
||||||
|
while True:
|
||||||
|
write_values(*read_values(run_upower()), datafile)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("datafile")
|
||||||
|
args = parser.parse_args()
|
||||||
|
run_loop(args.datafile)
|
Loading…
Reference in a new issue