no hypr folder + waybar cclock, nx_gcal_event

This commit is contained in:
Lennart J. Kurzweg (Nx2)
2024-04-18 18:41:53 +02:00
parent 91fccbf390
commit 8b2d527c23
8 changed files with 317 additions and 53 deletions

22
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File with Arguments",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": "${command:pickArgs}"
}
]
}

View File

@@ -50,42 +50,6 @@ in
pkgs-unstable.hyprlock
pkgs-unstable.hypridle
(pkgs.writeShellScriptBin "waybar_mode" ''
#!/bin/bash
# Function to print help message
print_help() {
echo "Usage: waybar_mode {set <string>|unset}"
}
if [ $# -lt 1 ]; then
print_help; exit 1;
fi
case "$1" in
set)
# Check if there is a second argument for the 'set' operation
if [ $# -eq 2 ]; then
echo "$2" > /tmp/waybar-mode
pkill -RTMIN+8 waybar
else
echo "Error: 'set' operation requires exactly one string argument."
print_help
exit 1
fi
;;
unset)
echo "" > /tmp/waybar-mode
pkill -RTMIN+8 waybar
;;
*)
echo "Error: Unknown command '$1'"
print_help
exit 1
;;
esac
exit 0
'')
];
wayland.windowManager.hyprland = {

View File

@@ -0,0 +1,222 @@
{ config, pkgs, secrets, ... }:
let in
{
home = {
file."${config.xdg.dataHome}/nx-gcal-event-credentials.json".text = ''
{
"installed": {
"client_id": "${secrets.nx-gcal-event.client-client-id}",
"project_id": "my-own-cal",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "${secrets.nx-gcal-event.client-secret}",
"redirect_uris": [
"http://localhost"
]
}
}
'';
packages = with pkgs; [
# TODO: make into real package, currently dependencies are in home.nix
# (pkgs.python311.withPackages (python-pkgs: [
# python-pkgs.google
# ]))
(writeScriptBin "nx_gcal_event" ''
#!${pkgs.python3}/bin/python3
import datetime
import os
import pickle
import sys
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from html import escape
CREDENTIALS_PATH = f"{os.environ['XDG_DATA_HOME']}/nx-gcal-event-credentials.json"
TOKEN_PATH = f"{os.environ['XDG_CACHE_HOME']}/nx-gcal-event-token.json"
PICKLE_PATH = "/tmp/nx-gcal-event.pickle"
def sec_to_nice_string(seconds: int):
(hours, rsec) = divmod(seconds, 3600)
minutes = rsec // 60
sep = " "
if hours == 0:
s_hours = f""
sep = ""
elif hours == 1: s_hours = f"{hours} hour"
else: s_hours = f"{hours} hours"
if minutes == 0:
s_minutes = f""
sep = ""
elif minutes == 1: s_minutes = f"{minutes} minute"
else: s_minutes = f"{minutes} minutes"
if hours + minutes == 0:
s_minutes = "~ No time"
os.remove(PICKLE_PATH)
return f"{s_hours}{sep}{s_minutes}"
def get_event_from_api():
creds = None
SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"]
if os.path.exists(TOKEN_PATH):
creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_PATH, SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open(TOKEN_PATH, "w") as token:
token.write(creds.to_json())
try:
service = build("calendar", "v3", credentials=creds)
now = datetime.datetime.utcnow().isoformat() + "Z" # 'Z' indicates UTC time
in_24_h = (datetime.datetime.utcnow() + datetime.timedelta(days=1)).isoformat() + "Z"
calendar_list = service.calendarList().list().execute()
calendars = calendar_list.get("items", [])
# List events from all calendars
all_events = []
for calendar in calendars:
calendar_id = calendar["id"]
events_result = service.events().list(
calendarId=calendar_id,
timeMin=now,
timeMax=in_24_h,
singleEvents=True,
orderBy="startTime",
).execute()
events = events_result.get("items", [])
all_events.extend(events)
# Filter out all-day events
all_events = [event for event in all_events if "dateTime" in event["start"]]
# Find the earliest event
earliest_event = None
for event in all_events:
event_start = event["start"]["dateTime"]
if not earliest_event or event_start < earliest_event["start"]["dateTime"]:
earliest_event = event
# Now earliest_event contains the event that starts earliest
return earliest_event
except HttpError as error:
print("An error occurred: %s" % error)
exit(1)
def dump_dict_to_file(event, now):
if not event:
event = {}
event['nxWriteTime'] = now
with open(PICKLE_PATH, 'wb') as f:
pickle.dump(event, f)
def load_dict_from_file(now):
with open(PICKLE_PATH, 'rb') as f:
event = pickle.load(f)
# recheck all 15 minutes
if (now - event['nxWriteTime']).seconds > 900:
event = get_event_from_api()
os.remove(PICKLE_PATH)
return event
def lookup():
# set now (timezone CEST)
now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=2)))
no_event_string = "~ Zen ~"
if os.path.exists(PICKLE_PATH):
event = load_dict_from_file(now)
else:
event = get_event_from_api()
if not event:
print(no_event_string)
dump_dict_to_file(event, now)
exit(0)
dump_dict_to_file(event, now)
try: # when the saved even was empty (there was no event)
end = datetime.datetime.strptime(event["end"]["dateTime"], "%Y-%m-%dT%H:%M:%S%z")
start = datetime.datetime.strptime(event["start"]["dateTime"], "%Y-%m-%dT%H:%M:%S%z")
except:
if (now - event['nxWriteTime']).seconds > 900:
event = get_event_from_api()
os.remove(PICKLE_PATH)
else:
print(no_event_string)
exit(0)
# set mode, remaining
if start.day != now.day: # event start tomorrow
print(no_event_string)
exit(0)
elif (start - now).days < 0: # today, started alredy
remaining = end - now
mode = " remaining in "
else: # today, not started yet
remaining = start - now
mode = " until the start of "
name = escape(event['summary'])
print(f"󱙬 {sec_to_nice_string(remaining.seconds)}{mode}\'{name}\'")
exit(0)
def print_help():
print("Usage: nx_gcal_event [lookup|force-lookup|reauthenicate|help]")
def forece_lookup():
try:
os.remove(PICKLE_PATH)
os.system('notify-send --app-name="nx_gcal_event" "Saved event deleted!"')
except:
os.system('notify-send --app-name="nx_gcal_event" "No saved event found!"')
finally:
lookup()
def reauthenicate():
os.remove(PICKLE_PATH)
os.remove(TOKEN_PATH)
os.system('notify-send --app-name="nx_gcal_event" "Deleted Token"')
lookup()
def print_help():
print("Usage: nx_gcal_event [lookup|force-lookup|reauthenicate|help]")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Incorrect number of arguments.")
print_help()
else:
arg = sys.argv[1]
if arg == "lookup":
lookup()
elif arg == "force-lookup":
forece_lookup()
elif arg == "reauthenticate":
reauthenicate()
elif arg == "help":
print_help()
else:
print_help()
'')
];
};
}

18
home-modules/python.nix Normal file
View File

@@ -0,0 +1,18 @@
{ config, pkgs, ... }:
let
python-with-packages = pkgs.python3.withPackages (pp: with pp; [
ipython
pipdeptree
requests
google google-api-python-client google-auth-httplib2 google-auth-oauthlib
]);
in
{
home.packages = [
python-with-packages
];
home.sessionVariables = {
PYTHONPATH = "${python-with-packages}/${python-with-packages.sitePackages}";
};
}

View File

@@ -2,8 +2,56 @@
let
in
{
imports = [
./nx-gcal-event.nix
];
home.packages = with pkgs; [
waybar
(pkgs.writeShellScriptBin "waybar_mode" ''
#!/usr/bin/env bash
print_help() {
echo "Usage: waybar_mode {set <string>|unset}"
}
if [ $# -lt 1 ]; then
print_help; exit 1;
fi
case "$1" in
set)
# Check if there is a second argument for the 'set' operation
if [ $# -eq 2 ]; then
echo "$2" > /tmp/waybar-mode
pkill -RTMIN+8 waybar
else
echo "Error: 'set' operation requires exactly one string argument."
print_help
exit 1
fi
;;
unset)
echo "" > /tmp/waybar-mode
pkill -RTMIN+8 waybar
;;
*)
echo "Error: Unknown command '$1'"
print_help
exit 1
;;
esac
exit 0
'')
(pkgs.writeShellScriptBin "cclock" ''
#!/bin/bash
#ord=$(date +"%e" | awk '{printf("%d%s\n", $1, substr("thstndrd", ($1%100-20)%10*2+1, 2))}')
ord=$(date +"%e" | awk '{printf("%d%s\n", $1, ($1==11||$1==12||$1==13)?"th":((($1%10)==1)?"st":((($1%10)==2)?"nd":((($1%10)==3)?"rd":"th"))))}')
if [ $# -eq 0 ]; then
echo "󰃮 $(date +'%A the')" "$ord" "of" "$(date +'%B')" " " "$(date +'%R')"
elif [ "$1" = "--no-icons" ]; then
echo "$(date +'%A the')" "$ord" "of" "$(date +'%B')" "$(date +'%R')"
fi
'')
];
programs.waybar = {
@@ -48,11 +96,11 @@ in
separate-outputs = true;
};
"custom/cclock" = {
exec = "/home/nx2/scripts/cclock.sh";
exec = "cclock";
restart-interval = 60;
};
"custom/ctimeremaining" = {
exec = "python /home/nx2/scripts/NxGCalEvent/get-remaining-time.py";
exec = "nx_gcal_event lookup";
restart-interval = 60;
};
"custom/mode" = {

View File

@@ -14,9 +14,9 @@
./home-modules/pnx/pnx.nix
# ./home-modules/hsmw.nix
./home-modules/hyprland/hyprland.nix
./home-modules/hyprland/hyprland-autoname-workspaces.nix
./home-modules/hyprland/waybar.nix
./home-modules/hyprland.nix
./home-modules/hyprland-autoname-workspaces.nix
./home-modules/waybar.nix
./home-modules/wlogout.nix
./home-modules/kitty.nix
@@ -31,6 +31,8 @@
./home-modules/theme/gtk.nix
./home-modules/theme/qt.nix
./home-modules/python.nix
];
home.username = "nx2";
home.homeDirectory = "/home/nx2";
@@ -66,18 +68,6 @@
gnumake
speedtest-go
(pkgs.python3.withPackages (python-pkgs: [
python-pkgs.ipython
python-pkgs.pipdeptree
python-pkgs.requests
]))
# (writeShellScriptBin "nxrbs-nix" ''
# set -e
# pushd ~/.nix-dots/
# git diff
# '')
];
xdg = {