Added space-misson-clock-style timer to eww

This commit is contained in:
2026-04-14 13:05:37 -06:00
parent 06815e7bc6
commit 7161892da7
15 changed files with 241 additions and 146 deletions

View File

@@ -12,25 +12,42 @@
color: $foreground;
font-family: "Armstrong";
font-size: 9pt;
background-color: rgba(0, 0, 0, 0);
background-color: rgba(30, 30, 30, 0.7);
padding: 10px;
border-width: 1px;
border-style: solid;
&:not(.focused) {
border-color: $bg0;
}
&.focused {
border-color: $foreground;
}
// Probably needs to change
&.bar {
margin: 10px;
margin-bottom: 0px;
}
&.bottombar {
margin: 10px;
margin-top: 0px;
}
&.left-side {
margin: 10px;
margin-top: 20px;
margin-top: 39px;
margin-right: 0px;
padding-bottom: 26px;
margin-bottom: 39px;
}
&.right-side {
margin: 10px;
margin-top: 20px;
margin-top: 39px;
margin-left: 0px;
padding-bottom: 26px;
margin-bottom: 39px;
}
&.bg {
@@ -73,7 +90,7 @@
}
.gauge-gutter {
color: $bg0;
color: rgba(30, 30, 30, 0.5);
}
.icon {
@@ -81,7 +98,7 @@
}
.invisible {
color: $wallpaper;
color: rgba(0, 0, 0, 0);
}
.highlight {
@@ -122,7 +139,8 @@
// sway module
.sway--root.sway--vertical {
padding-top: 20px;
padding-top: 10px;
padding-bottom: 10px;
}
.sway--ws {

View File

@@ -18,9 +18,9 @@
:anchor "top center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(centerbox :orientation "h"
:class "bar root"
:class "bar root ${sway--data.visible[group].focused ? 'focused' : ''}"
(hostname-leftalign)
''
(ws-group-rightalign :workspace-group {group})))
@@ -31,9 +31,9 @@
:anchor "top center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(centerbox :orientation "h"
:class "bar root"
:class "bar root ${sway--data.visible[group].focused ? 'focused' : ''}"
(ws-group-leftalign :workspace-group {group})
(hostname-centeralign)
(desktop-details-rightalign)))
@@ -44,9 +44,9 @@
:anchor "top center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(centerbox :orientation "h"
:class "bar root"
:class "bar root ${sway--data.visible[group].focused ? 'focused' : ''}"
(ws-group-leftalign :workspace-group {group})
''
(hostname-rightalign)))
@@ -57,8 +57,14 @@
:anchor "${side} center")
:exclusive true
:focusable false
:stacking "fg"
(system-sidebar :orientation {side}))
:stacking "bg"
(centerbox :orientation "v"
;; :class "root ${side}-side ${sway--data.visible[group].focused ? 'focused' : ''}"
(system-sidebar :group {group}
:side {side})
""
""
))
(defwindow user-sidebar [group side]
:geometry (geometry :width "200px"
@@ -66,7 +72,7 @@
:anchor "${side} center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(user-sidebar :orientation {side}))
@@ -76,9 +82,9 @@
:anchor "top center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(centerbox :orientation "h"
:class "bar root"
:class "laptop bar root ${sway--data.visible[group].focused ? 'focused' : ''}"
(hostname-and-workspace-leftalign :workspace-group {group})
""
(laptop-details-rightalign :battery {battery} )))
@@ -89,10 +95,14 @@
:anchor "${side} center")
:exclusive true
:focusable false
:stacking "fg"
(small-sidebar :orientation {side}
:battery {battery}
:workspace-group {group}))
:stacking "bg"
(centerbox :orientation "v"
(laptop-sidebar-top :group {group}
:side {side})
""
(laptop-sidebar-bottom :group {group}
:side {side}
:battery {battery})))
(defwindow vertical-bottombar [group]
:geometry (geometry :width "100%"
@@ -100,9 +110,9 @@
:anchor "bottom center")
:exclusive true
:focusable false
:stacking "fg"
:stacking "bg"
(centerbox :orientation "h"
:class "bar root"
:class "bottombar root ${sway--data.visible[group].focused ? 'focused' : ''}"
(date)
(big-clock)
(horizontal-minigauges-rightalign)))

View File

@@ -21,6 +21,26 @@
(label :text {formattime(clock--data.stamp, "%Y-%m-%d", clock--data.tz)})))
(defwidget mission-clock []
(box :class "module text"
:spacing 0
:orientation "v"
:halign "center"
:valign "center"
:visible {clock--data.timer.active}
(box :orientation "h"
:space-evenly false
:spacing 5
:valign "center"
:halign "center"
(label :text 'T'
:class "special")
(label :text {clock--data.timer.started ? "+" : "-"}
:class {clock--data.timer.started ? "green" : "highlight"})
(label :text {clock--data.timer.timer}
:class "special"))
(label :text '${clock--data.timer.title} ${clock--data.timer.prefix}')))
(defwidget big-clock []
(centerbox :class "bigger nebula"
:space-evenly false
@@ -46,9 +66,32 @@
(label :text {formattime(clock--data.stamp, "%B %d", clock--data.tz)}
:class "special"
:valign "center")))
(defwidget side-mission-clock []
(box :class "module text"
:space-evenly false
:spacing 5
:halign "start"
:valign "center"
:visible {clock--data.timer.active}
:width 200
:orientation "v"
(label :class "big nebula special"
:text {clock--data.timer.title})
(label :class "nebula"
:text {clock--data.timer.prefix})
(box :class "nebula"
:spacing 5
:halign "center"
:space-evenly false
(label :text "T")
(label :class {clock--data.timer.started ? "green" : "highlight"}
:text {clock--data.timer.started ? "+" : "-"})
(label :class "nebula"
:text {clock--data.timer.timer}))))
(defwidget sideclock []
(button :onclick "echo -n $(date +%Y-%d-%m) | wl-copy && eww update clock--show=date && sleep 2 && eww update clock--show=clock"
:onrightclick "echo -n $(date +%s) | wl-copy && eww update clock--show=unixtime && sleep 2 && eww update clock--show=clock"

View File

@@ -11,7 +11,7 @@ import time
import asyncio
from asyncinotify import Inotify, Mask
from pathlib import Path
from json import dump
from json import load, dump
def refresh_tz():
@@ -29,7 +29,55 @@ def refresh_tz():
return tz, source
def refresh_timer():
"""Reload the timer."""
timer_file = Path("~/.config/eww/timer.json").expanduser()
if timer_file.exists() and timer_file.is_file():
with timer_file.open("r") as f:
return load(f)
return None
timezone, source = refresh_tz()
timer = refresh_timer()
def format_timer():
"""Format the timer delta."""
if timer is None:
return {"active": False, "error": False, "reason": "no timer"}
startstamp = timer.get("start")
if startstamp is None:
return {"active": False, "error": True, "reason": "no start"}
start = datetime.datetime.fromtimestamp(startstamp)
now = datetime.datetime.now()
if (stopstamp := timer.get("stop")) is not None:
stop = datetime.datetime.fromtimestamp(stopstamp)
if stop < now:
return {"active": False, "error": False, "reason": f"stopped at {stop}"}
delta_sec = (now - start).total_seconds()
started = True
if delta_sec < 0:
started = False
delta = datetime.timedelta(seconds=int(abs(delta_sec)))
else:
delta = datetime.timedelta(seconds=int(delta_sec))
days = delta.days
remainder = datetime.timedelta(seconds=delta.total_seconds() - (days * 24 * 3600))
return {
"active": True,
"error": False,
"prefix": timer.get("prefix", "MET"),
"title": timer.get("title", "timer"),
"started": started,
"timer": f"{days}d {str(remainder)}",
}
async def main():
@@ -41,6 +89,7 @@ async def main():
"stamp": int(datetime.datetime.now().timestamp()),
"tz": timezone,
"tz-source": source,
"timer": format_timer(),
},
sys.stdout,
)
@@ -51,15 +100,16 @@ async def main():
async def monitor():
"""Timezone monitor loop."""
global timezone, source
global timezone, source, timer
with Inotify() as inotify:
inotify.add_watch(
Path("~/.config/eww").expanduser(),
Mask.CREATE | Mask.DELETE,
Mask.CREATE | Mask.DELETE | Mask.MODIFY,
)
inotify.add_watch(Path("/etc"), Mask.CREATE | Mask.DELETE)
async for event in inotify:
timezone, source = refresh_tz()
timer = refresh_timer()
if __name__ == "__main__":

View File

@@ -6,11 +6,13 @@ import json
import asyncio
import sys
PRIORITY_PLAYERS = {"Feishin"}
# Override the reported album if the URL is in this dict
URL_ALBUM_OVERRIDES = {"https://distantworlds3.space/radio/": "Distant Radio"}
URL_ALBUM_OVERRIDES = {
"https://distantworlds3.space/radio/": "Distant Radio",
"https://radio.distantworlds3.space/listen/distant_radio/distantradio.mp3": "Distant Radio",
}
class MprisMonitor(Gio.Application):

View File

@@ -120,12 +120,13 @@ def get_gateways():
return result
def get_ssid():
cmd = ['iwgetid', '-r']
cmd = ["iwgetid", "-r"]
result = run(cmd, stdout=PIPE, stderr=PIPE)
if result.returncode == 0:
return result.stdout.decode('utf-8').strip()
return result.stdout.decode("utf-8").strip()
else:
return None
@@ -180,7 +181,7 @@ def get_public_ip():
def get_default_route():
"""Get the default route."""
cmd = ["ip", "route", "show", "default"]
cmd = ["ip", "route", "get", "1.1.1.1"]
result = run(cmd, stdout=PIPE, stderr=PIPE)
try:
# Get first line (might have multiple gateway routes)
@@ -205,6 +206,7 @@ def format_time(time: datetime) -> dict[str, str | int]:
"unix": int(time.timestamp()),
}
runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "/tmp")
@@ -299,7 +301,7 @@ while True:
"wifi": {
"ssid": ssid,
"connected": wifi_connected,
"default": default_route == "wlan0"
"default": default_route == "wlan0",
},
"last_update": format_time(datetime.now()),
}

View File

@@ -1,56 +1,51 @@
#!/usr/bin/env python3
# This script prints the time remaining for the current timer
# as stored in the file ~/.timer as a binary unix timestamp.
# Produces a mission timer based on the start time stored in the ~/.config/eww/timer.json file.
# This file should have an object with three fields: "start", containing the start time as an integer
# Unix timestamp in second resolution, "title", containing the title to be presented beneath the timer,
# and "prefix", containing a string to render in front of the timer (such as "MET")
import time
import datetime
import json
import sys
import asyncio
from asyncinotify import Inotify, Mask
from pathlib import Path
# Get the time stored in the file ~/.timer
try:
with open("/home/ezri/.timer", "br") as f:
timer = f.read()
except FileNotFoundError:
print("It's Time!", flush=True)
exit()
# Convert the binary unix timestamp to a datetime object
timer = datetime.datetime.fromtimestamp(int.from_bytes(timer, "big"))
def refresh_timer():
"""Reload the timer."""
while True:
# Get the current time
now = datetime.datetime.now()
timer_file = Path("~/.config/eww/timer.json")
if timer_file.exists() and timer_file.is_file():
with timer_file.open("r") as f:
return json.load(f)
return None
# Calculate the time remaining
remaining = timer - now
# If the timer has already expired, delete the file and exit
if remaining.total_seconds() <= 0:
import os
timer = refresh_timer()
os.remove("/home/ezri/.timer")
print("Timer expired.")
# Print the time remaining in the format "D days, HH:MM:SS"
if remaining.days == 0:
print(
"{} hours {:02d}:{:02d}".format(
remaining.seconds // 3600,
remaining.seconds % 3600 // 60,
remaining.seconds % 60,
),
flush=True,
)
else:
print(
"{} days, {:02d}:{:02d}:{:02d}".format(
remaining.days,
remaining.seconds // 3600,
remaining.seconds % 3600 // 60,
remaining.seconds % 60,
),
flush=True,
)
# Wait half a second before printing the time again
time.sleep(1)
async def main():
"""Print loop."""
asyncio.ensure_future(asyncio.create_task(monitor()))
while True:
if timer is None:
json.dump({"active": False}, sys.stdout)
else:
start = timer.get("start", 0)
now = datetime.datetime.now()
if now < start:
started = True
else:
started = False
json.dump(
{
"active": True,
"stamp": timer.get("start", 0),
"title": timer.get("title", "timer"),
"prefix": timer.get("prefix", "MET"),
},
sys.stdout,
)

5
.config/eww/timer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"title": "DW3",
"prefix": "expedition clock",
"start": 1768759200
}

View File

@@ -6,13 +6,14 @@
;; Generic Windows ;;
;;; ;;;
(defwidget system-sidebar [orientation]
(defwidget system-sidebar [group side]
(box :orientation "v"
:valign "start"
:space-evenly false
:spacing 20
:class "root ${orientation}-side"
:class "root ${side}-side ${sway--data.visible[group].focused ? 'focused' : ''}"
(sideclock)
(side-mission-clock)
(network-sidebar-details)
(system-gauges)
(volume-gauges)))
@@ -27,23 +28,37 @@
(mpris-miniplayer)
(volume-gauges)))
(defwidget small-sidebar [orientation battery workspace-group]
(centerbox :orientation "v"
:class "root ${orientation}-side"
(sway-workspaces-vertical :group {workspace-group})
""
(box :orientation "v"
:valign "end"
:space-evenly false
:spacing 10
(system--cpu-gauge-small)
(system--memory-gauge-small)
(system--swap-gauge-small)
(system--gpu-gauge-small)
(system--vram-gauge-small)
(volume-small-gauge)
(system-battery-gauge-small :battery {battery}))))
(defwidget minigauges-bottomalign [battery]
(box :orientation "v"
:valign "end"
:space-evenly false
:spacing 10
(system--cpu-gauge-small)
(system--memory-gauge-small)
(system--swap-gauge-small)
(system--gpu-gauge-small)
(system--vram-gauge-small)
(volume-small-gauge)
(system-battery-gauge-small :battery {battery})))
(defwidget laptop-sidebar-top [group side]
(box :orientation "v"
:valign "start"
:space-evenly false
:spacing 10
:class "root ${side}-side ${sway--data.visible[group].focused ? 'focused' : ''}"
(sway-workspaces-vertical :group {group})))
(defwidget laptop-sidebar-bottom [group side battery]
(box :orientation "v"
:valign "start"
:space-evenly false
:spacing 10
:class "root ${side}-side ${sway--data.visible[group].focused ? 'focused' : ''}"
(minigauges-bottomalign :battery {battery})))
(defwidget ws-group-rightalign [workspace-group]
(box :orientation "h"
@@ -71,6 +86,7 @@
:spacing 20
:class "rightbox"
(mpris2)
(mission-clock)
(vpn-network)
(network)
(wifi)

View File

@@ -1,13 +0,0 @@
[Unit]
Description=AggieTimeD
[Service]
Type=simple
ExecStartPre=-/usr/bin/rm /run/user/%U/aggietimed.sock
ExecStartPre=/usr/bin/sleep 1
ExecStart=/usr/bin/node /usr/bin/aggietimed -d -s /run/user/%U/aggietimed.sock -p echo
RestartSec=30
Restart=always
[Install]
WantedBy=sway-session.target

View File

@@ -1,10 +0,0 @@
[Unit]
Description=Socket-activation proxy for alacritty
PartOf=graphical-session.target
BindsTo=alacritty.service
After=alacritty.service
[Service]
Type=notify
ExecStart=/usr/lib/systemd/systemd-socket-proxyd %t/alacritty-direct
Slice=session.slice

View File

@@ -1,7 +0,0 @@
[Unit]
Description=Socket activation for alacritty
PartOf=graphical-session
[Socket]
ListenStream=%t/alacritty
SocketMode=0600

View File

@@ -1,11 +0,0 @@
[Unit]
Description=dunst notification daemon
PartOf=sway-session.target
[Service]
Type=simple
ExecStart=dunst
Slice=session.slice
[Install]
WantedBy=sway-session.target

View File

@@ -1,7 +0,0 @@
[Unit]
Description=Hyprland compositor session
Documentation=man:systemd.special
BindsTo=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.target
Wants=sway-session.target

View File

@@ -0,0 +1,2 @@
[Unit]
Description=Displays are turned off