186 lines
5.5 KiB
Python
186 lines
5.5 KiB
Python
import os
|
|
import signal
|
|
import threading
|
|
from subprocess import Popen, PIPE
|
|
|
|
from i3pystatus import IntervalModule, formatp
|
|
|
|
|
|
class RedshiftController(threading.Thread):
|
|
|
|
def __init__(self, args=[]):
|
|
"""Initialize controller and start child process
|
|
|
|
The parameter args is a list of command line arguments to pass on to
|
|
the child process. The "-v" argument is automatically added."""
|
|
|
|
threading.Thread.__init__(self)
|
|
|
|
# Initialize state variables
|
|
self._inhibited = False
|
|
self._temperature = 0
|
|
self._period = 'Unknown'
|
|
self._location = (0.0, 0.0)
|
|
self._brightness = 0.0
|
|
self._pid = None
|
|
|
|
cmd = ["redshift"] + args
|
|
if "-v" not in cmd:
|
|
cmd += ["-v"]
|
|
|
|
env = os.environ.copy()
|
|
env['LANG'] = env['LANGUAGE'] = env['LC_ALL'] = env['LC_MESSAGES'] = 'C'
|
|
|
|
self._params = {
|
|
"args": cmd,
|
|
"env": env,
|
|
"bufsize": 1,
|
|
"stdout": PIPE,
|
|
"universal_newlines": True,
|
|
}
|
|
|
|
def parse_output(self, line):
|
|
"""Convert output to key value pairs"""
|
|
|
|
try:
|
|
key, value = line.split(":")
|
|
self.update_value(key.strip(), value.strip())
|
|
except ValueError:
|
|
pass
|
|
|
|
def update_value(self, key, value):
|
|
"""Parse key value pairs to update their values"""
|
|
|
|
if key == "Status":
|
|
self._inhibited = value != "Enabled"
|
|
elif key == "Color temperature":
|
|
self._temperature = int(value.rstrip("K"), 10)
|
|
elif key == "Period":
|
|
self._period = value
|
|
elif key == "Brightness":
|
|
self._brightness = value
|
|
elif key == "Location":
|
|
location = []
|
|
for x in value.split(", "):
|
|
v, d = x.split(" ")
|
|
location.append(float(v) * (1 if d in "NE" else -1))
|
|
self._location = (location)
|
|
|
|
@property
|
|
def inhibited(self):
|
|
"""Current inhibition state"""
|
|
return self._inhibited
|
|
|
|
@property
|
|
def temperature(self):
|
|
"""Current screen temperature"""
|
|
return self._temperature
|
|
|
|
@property
|
|
def period(self):
|
|
"""Current period of day"""
|
|
return self._period
|
|
|
|
@property
|
|
def location(self):
|
|
"""Current location"""
|
|
return self._location
|
|
|
|
@property
|
|
def brightness(self):
|
|
"""Current brightness"""
|
|
return self._brightness
|
|
|
|
def set_inhibit(self, inhibit):
|
|
"""Set inhibition state"""
|
|
if self._pid and inhibit != self._inhibited:
|
|
os.kill(self._pid, signal.SIGUSR1)
|
|
self._inhibited = inhibit
|
|
|
|
def run(self):
|
|
with Popen(**self._params) as proc:
|
|
self._pid = proc.pid
|
|
for line in proc.stdout:
|
|
self.parse_output(line)
|
|
proc.wait(10)
|
|
|
|
|
|
class Redshift(IntervalModule):
|
|
"""
|
|
Show status and control redshift - http://jonls.dk/redshift/.
|
|
|
|
This module runs an instance of redshift by itself, since it needs to parse
|
|
its output, so you should remove redshift/redshift-gtk from your i3 config
|
|
before using this module.
|
|
|
|
Requires `redshift` installed.
|
|
|
|
.. rubric:: Available formatters
|
|
|
|
* `{inhibit}` — show if redshift is currently On or Off (using `toggle_inhibit` callback)
|
|
* `{latitude}` — location latitude
|
|
* `{longitude}` — location longitude
|
|
* `{period}` — current period (Day or Night)
|
|
* `{temperature}` — current screen temperature in Kelvin scale (K)
|
|
|
|
"""
|
|
settings = (
|
|
("color", "Text color"),
|
|
("error_color", "Text color when an error occurs"),
|
|
"format",
|
|
("format_inhibit",
|
|
"List of 2 strings for `{inhibit}`, the first is shown when Redshift is On and the second is shown when Off"),
|
|
("redshift_parameters", "List of parameters to pass to redshift binary"),
|
|
)
|
|
|
|
color = "#ffffff"
|
|
error_color = "#ff0000"
|
|
format = "{inhibit} {temperature}K"
|
|
format_inhibit = ["On", "Off"]
|
|
on_leftclick = "toggle_inhibit"
|
|
redshift_parameters = []
|
|
|
|
def init(self):
|
|
self._controller = RedshiftController(self.redshift_parameters)
|
|
self._controller.daemon = True
|
|
self._controller.start()
|
|
self.update_values()
|
|
|
|
def update_values(self):
|
|
self.inhibit = self._controller.inhibited
|
|
self.period = self._controller.period
|
|
self.temperature = self._controller.temperature
|
|
self.latitude, self.longitude = self._controller.location
|
|
self.brightness = self._controller.brightness
|
|
|
|
def toggle_inhibit(self):
|
|
"""Enable/disable redshift"""
|
|
if self.inhibit:
|
|
self._controller.set_inhibit(False)
|
|
self.inhibit = False
|
|
else:
|
|
self._controller.set_inhibit(True)
|
|
self.inhibit = True
|
|
|
|
def run(self):
|
|
if self._controller.is_alive():
|
|
self.update_values()
|
|
fdict = {
|
|
"inhibit": self.format_inhibit[int(self.inhibit)],
|
|
"period": self.period,
|
|
"temperature": self.temperature,
|
|
"latitude": self.latitude,
|
|
"longitude": self.longitude,
|
|
"brightness": self.brightness,
|
|
}
|
|
output = formatp(self.format, **fdict)
|
|
color = self.color
|
|
else:
|
|
output = "redshift exited unexpectedly"
|
|
color = self.error_color
|
|
|
|
self.output = {
|
|
"full_text": output,
|
|
"color": color,
|
|
}
|