Refactor systemctl services and timers

- Unified service templates into generic systemctl templates
- Introduced reusable filter plugins for script path handling
- Updated path variables and service/timer definitions
- Migrated roles (backup, cleanup, repair, etc.) to use systemctl role
- Added sys-daemon role for core systemd cleanup
- Simplified timer handling via sys-timer role

Note: This is a large refactor and some errors may still exist. Further testing and adjustments will be needed.
This commit is contained in:
2025-08-18 21:22:16 +02:00
parent 3a839cfe37
commit 2569abc0be
219 changed files with 618 additions and 1104 deletions

View File

@@ -0,0 +1,31 @@
# MSI Keyboard Driver
Ansible role to set up dynamic keyboard color change on MSI laptops.
## Requirements
- An MSI laptop
- The `msi-perkeyrgb` tool installed on the system
- Ansible 2.9 or later
## Role Variables
Available variables are listed below, along with their default values:
```yaml
vendor_and_product_id: ""
```
The `vendor_and_product_id` variable is required and should be set to the vendor and product ID of the MSI laptop.
## Author
This role was created by [Kevin Veen-Birkenbach](https://github.com/kevinveenbirkenbach).
## Chat Conversation
To see how this role was developed, you can refer to the following ChatGPT Conversation that produced this software:
- https://chat.openai.com/share/1f1dde28-3fb2-4df7-be89-4f7ffe67e5c7
- https://chat.openai.com/share/41c47fdb-a92d-466d-9e92-5a894fe6bec3
- https://chat.openai.com/share/e45d186a-f2e1-4c96-9390-36269e274193

View File

@@ -0,0 +1,2 @@
# Todo
- Implement schema

View File

@@ -0,0 +1,139 @@
import sys
import datetime
import subprocess
def hex_to_rgb(hex_color):
""" Converts a hexadecimal color string to an RGB tuple.
Args:
hex_color (str): Hexadecimal color string.
Returns:
tuple: Tuple representing the RGB color.
"""
if len(hex_color) != 6:
raise ValueError("Hex color must be 6 characters long.")
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def calculate_color(start_color, end_color, ratio):
""" Calculates the interpolated color between two colors.
Args:
start_color (str): Start color in hex format.
end_color (str): End color in hex format.
ratio (float): Interpolation ratio (0 to 1).
Returns:
str: Interpolated color in hex format.
"""
start_rgb = hex_to_rgb(start_color)
end_rgb = hex_to_rgb(end_color)
current_rgb = [round(start + (end - start) * ratio) for start, end in zip(start_rgb, end_rgb)]
return ''.join(f"{value:02x}" for value in current_rgb)
def get_current_period(current_time, color_times):
"""
Determines the current time period and returns the corresponding colors.
Args:
current_time (datetime.time): The current time.
color_times (dict): A dictionary linking time periods (as a string in 'HH:MM' format)
with pairs of colors.
Returns:
tuple: A tuple of the start and end color (as hex codes) for the current period.
"""
sorted_periods = sorted(color_times.items())
for i, (period_start_str, colors) in enumerate(sorted_periods):
period_start_time = datetime.datetime.strptime(period_start_str, "%H:%M").time()
if current_time <= period_start_time:
return sorted_periods[i - 1 if i > 0 else -1][1]
return sorted_periods[0][1]
def calculate_transition_ratio(current_time, start_time, end_time):
"""
Calculates the transition ratio for a color transition.
Args:
current_time (datetime.time): The current time.
start_time (datetime.time): The start time of the current color period.
end_time (datetime.time): The end time of the current color period.
Returns:
float: The transition ratio between 0 and 1.
"""
# Use the current date for timestamp calculation
today = datetime.datetime.now().date()
# Calculate timestamps for the start and end of the current period
start_timestamp = datetime.datetime.combine(today, start_time).timestamp()
end_timestamp = datetime.datetime.combine(today, end_time).timestamp()
# If start and end times are the same, return 0 to indicate no transition
if start_timestamp == end_timestamp:
return 0
# Calculate the current timestamp
current_timestamp = datetime.datetime.combine(today, current_time).timestamp()
# Calculate the transition ratio
transition_duration = end_timestamp - start_timestamp
time_since_start = current_timestamp - start_timestamp
# Ratio as the proportion of time elapsed in the total transition duration
return time_since_start / transition_duration
def change_keyboard_color(color, device_id):
""" Changes the keyboard color using an external command.
Args:
color (str): Color in hex format.
device_id (str): Vendor and product ID for the keyboard.
"""
command = ["msi-perkeyrgb", "--model", "GS65", "-s", color, "--id", device_id]
try:
subprocess.run(command, check=True)
print(f"Keyboard color changed to #{color}.")
except subprocess.CalledProcessError as e:
print(f"Error setting keyboard color: {e}")
sys.exit(1)
def main(vendor_and_product_id):
color_times = {
"22:00": ("990000", "990000"), # Night
"21:00": ("0000ff", "990000"), # Evening Blue to Night
"20:00": ("fdbe51", "0000ff"), # Evening Golden to Blue
"19:30": ("ff4500", "fdbe51"), # Sunset to Golden
"12:00": ("ffffff", "ff4500"), # Noon to Sunset
"07:30": ("ffd700", "ffffff"), # Sunrise to Noon
"07:00": ("f9c80e", "ffd700"), # Morning Golden to Sunrise
"06:00": ("ff7f00", "f9c80e"), # Dawn to Golden
"05:00": ("5bc0eb", "ff7f00"), # Morning Blue to Dawn
"04:00": ("990000", "5bc0eb"), # Night to Blue
}
current_time = datetime.datetime.now().time()
start_color, end_color = get_current_period(current_time, color_times)
sorted_times = sorted(color_times.keys())
for time_str in sorted_times + [sorted_times[0]]:
next_start_time_obj = datetime.datetime.strptime(time_str, "%H:%M").time()
if current_time < next_start_time_obj:
break
current_period_start_time_str = [time for time, colors in color_times.items() if colors == (start_color, end_color)][0]
current_period_start_time_obj = datetime.datetime.strptime(current_period_start_time_str, "%H:%M").time()
transition_ratio = calculate_transition_ratio(current_time, current_period_start_time_obj, next_start_time_obj)
current_color = calculate_color(start_color, end_color, transition_ratio)
change_keyboard_color(current_color, vendor_and_product_id)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Error: Vendor and product ID is missing.")
print("Usage: python keyboard_color.py <vendor_and_product_id>")
sys.exit(1)
vendor_and_product_id = sys.argv[1]
main(vendor_and_product_id)

View File

@@ -0,0 +1,9 @@
galaxy_info:
role_name: keyboard-color
author: Kevin Veen-Birkenbach
description: "Ansible role to set up dynamic keyboard color change on MSI laptops"
min_ansible_version: 2.9
platforms:
- name: Linux
versions:
- all

View File

@@ -0,0 +1 @@
vendor_and_product_id: "" # @todo schema needs to be implemented

View File

@@ -0,0 +1,18 @@
- include_role:
name: '{{ item }}'
loop:
- dev-yay
- sys-ctl-alm-compose
- name: Install MSI packages
kewlfft.aur.aur:
use: yay
name:
- msi-perkeyrgb
- include_role:
name: sys-systemctl
vars:
systemctl_on_calendar: "{{ SYS_SCHEDULE_ANIMATION_KEYBOARD_COLOR }}"
systemctl_timer_enabled: true
persistent: true

View File

@@ -0,0 +1,5 @@
- block:
- include_tasks: 01_core.yml
- set_fact:
run_once_svc_opt_keyboard_color: true
when: run_once_svc_opt_keyboard_color is not defined

View File

@@ -0,0 +1,7 @@
[Unit]
Description=Keyboard Color Service
OnFailure={{ SYS_SERVICE_ON_FAILURE_COMPOSE }}
[Service]
Type=oneshot
ExecStart=/bin/python {{ systemctl_id | get_service_script_path('py') }} {{ vendor_and_product_id }}

View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Pfad zum Keyboard-Color-Skript
KEYBOARD_COLOR_SCRIPT="$1"
DEVICE_ID="$2"
# NTP deaktivieren
timedatectl set-ntp false
# Startzeit speichern
START_TIME=$(date +%s)
# 2 Minuten (120 Sekunden) für den gesamten Tag simulieren
for i in {0..60}; do
# Berechnung der aktuellen simulierten Zeit
CURRENT_TIME=$((START_TIME + (( 86400 / 60 ) * i )))
# Systemzeit auf die simulierte Zeit setzen
date +%s -s "@$CURRENT_TIME"
# Keyboard-Color-Skript ausführen
python $KEYBOARD_COLOR_SCRIPT $DEVICE_ID
# 2 Sekunden Pause
sleep 2
done
# NTP wieder aktivieren
timedatectl set-ntp true

View File

@@ -0,0 +1,3 @@
application_id: svc-opt-keyboard-color
systemctl_id: "{{ application_id }}"
vendor_and_product_id: "{{ applications | get_app_conf(application_id, 'vendor_and_product_id') }}"