Compare commits

...

10 Commits

Author SHA1 Message Date
347c87a154 fix date 2025-01-15 19:05:55 +01:00
9128e8ce87 add CPU temp to standby display 2025-01-13 23:47:46 +01:00
a9e3bfcf98 playlist fix 2025-01-12 04:08:05 +01:00
e3a1680c90 added systemd daemon file 2025-01-12 02:55:10 +01:00
7100dcea8b small layout update 2025-01-11 19:30:45 +01:00
a9ea1e4d2a update config 2025-01-11 19:29:02 +01:00
5bcaa84bfb update layout 2025-01-11 19:16:12 +01:00
3cc831ba72 update color scheme config & layout 2025-01-11 19:08:52 +01:00
0627a480f3 update layout 2025-01-11 19:04:17 +01:00
9765756d7b fix bar length on song switch 2025-01-11 18:57:23 +01:00
5 changed files with 64 additions and 31 deletions

14
spotiplayer.service Normal file
View File

@ -0,0 +1,14 @@
[Unit]
Description=Spotiplayer
After=network.target
[Service]
Type=simple
WorkingDirectory=/path/to/this/repository
ExecStart=/usr/bin/python3 -m spotiplayer_pi.main
Restart=on-failure
User=root
[Install]
WantedBy=multi-user.target

View File

@ -39,7 +39,7 @@ class Api:
return self._format_req(req.json()) return self._format_req(req.json())
def _format_req(self, r): def _format_req(self, r):
if not r["is_playing"]: if not r["is_playing"] or r["currently_playing_type"] != "track":
return "not-playing" return "not-playing"
item, album = r["item"], r["item"]["album"] item, album = r["item"], r["item"]["album"]
res = { res = {

View File

@ -1,12 +1,11 @@
refresh_token: "AQBGNOL-iocPZ_zxtr_sNXx4BZRaxPmg2Ea0cJGorcEu2IW2wSnLdkYDJpVDOuQQHWIQ-LjuNpBcY9PurPtdfPoT5ljXnGpso9CqX6n2alBXaYc767oKya_52qxqWPE0o7k" refresh_token: "AQBGNOL-iocPZ_zxtr_sNXx4BZRaxPmg2Ea0cJGorcEu2IW2wSnLdkYDJpVDOuQQHWIQ-LjuNpBcY9PurPtdfPoT5ljXnGpso9CqX6n2alBXaYc767oKya_52qxqWPE0o7k"
base_64: "ZTI5Mzg3NWI3YWQ5NDQzZDg1NDMxYzkwOTEyZTdiMWQ6MjlmNDEzZTJiOTRlNGU0NDlkOWRiYmU0NmM2OTY5MTY=" base_64: "ZTI5Mzg3NWI3YWQ5NDQzZDg1NDMxYzkwOTEyZTdiMWQ6MjlmNDEzZTJiOTRlNGU0NDlkOWRiYmU0NmM2OTY5MTY="
api_interval: 5 # (float) number of seconds in-between API requests api_interval: 3 # (float) number of seconds in-between API requests
refresh_interval: 0.4 # (float) number of seconds in-between progress bar refreshes refresh_interval: 0.4 # (float) number of seconds in-between progress bar refreshes
# Set Color Theme # Set Color Theme
color_theme: color_theme:
text: [253, 238, 216] # [R, G, B] All text components text: [253, 238, 216] # [R, G, B] All text components and bar outline
bar_outline: [16, 81, 86] # [R, G, B] Bar in-fill bar_inside: [30, 215, 96] # [R, G, B] Bar in-fill
bar_inside: [148, 191, 136] # [R, G, B] Bar in-fill
background: [13, 19, 33] # [R, G, B] Background background: [13, 19, 33] # [R, G, B] Background

View File

@ -7,11 +7,28 @@ import warnings
from datetime import datetime from datetime import datetime
import yaml import yaml
from typing import Dict from typing import Dict
import subprocess
from spotiplayer_pi.lib import LCD_2inch from spotiplayer_pi.lib import LCD_2inch
from spotiplayer_pi.api import Api from spotiplayer_pi.api import Api
def get_cpu_temp():
try:
result = subprocess.run(
["vcgencmd", "measure_temp"], capture_output=True, text=True, check=True
)
temp_str = result.stdout.strip()
temp_value = temp_str.split("=")[1].replace("'C", "")
return float(temp_value)
except subprocess.CalledProcessError as e:
warnings.warn(f"Error executing vcgencmd: {e}")
return "None"
except (IndexError, ValueError) as e:
warnings.warn(f"Error parsing temperature: {e}")
return "None"
def parse_config(path="spotiplayer_pi/config.yaml"): def parse_config(path="spotiplayer_pi/config.yaml"):
with open(path, "r") as file: with open(path, "r") as file:
data = yaml.safe_load(file) data = yaml.safe_load(file)
@ -28,7 +45,7 @@ def display_loop(api: Api, cfg: Dict):
bg = Image.new("RGB", (d.height, d.width), (0, 0, 0)) bg = Image.new("RGB", (d.height, d.width), (0, 0, 0))
d.ShowImage(bg) d.ShowImage(bg)
Font0 = ImageFont.truetype("Font/GothamMedium.ttf", 13) Font0 = ImageFont.truetype("Font/GothamBold.ttf", 14)
Font1 = ImageFont.truetype("Font/GothamMedium.ttf", 18) Font1 = ImageFont.truetype("Font/GothamMedium.ttf", 18)
Font1b = ImageFont.truetype("Font/GothamBold.ttf", 19) Font1b = ImageFont.truetype("Font/GothamBold.ttf", 19)
Font2 = ImageFont.truetype("Font/GothamMedium.ttf", 20) Font2 = ImageFont.truetype("Font/GothamMedium.ttf", 20)
@ -52,7 +69,6 @@ def display_loop(api: Api, cfg: Dict):
"Connect to speakers", "Connect to speakers",
font=Font2, font=Font2,
fill=(255, 255, 255), fill=(255, 255, 255),
# fill=cfg["color_theme"]["text"],
) )
del spoti_logo, qr, draw del spoti_logo, qr, draw
@ -78,22 +94,26 @@ def display_loop(api: Api, cfg: Dict):
elif data == "not-playing": elif data == "not-playing":
draw = ImageDraw.Draw(not_playing_img) draw = ImageDraw.Draw(not_playing_img)
current_time = datetime.now().time() current_time = datetime.now()
draw.rectangle([(00, 120), (119, 170)], fill=(0, 0, 0)) draw.rectangle([(00, 120), (119, 190)], fill=(0, 0, 0))
offset = 10 if current_time.hour < 9 else 0 offset = 10 if current_time.hour < 9 else 0
draw.text( draw.text(
(10 + offset, 120), (10 + offset, 120),
f"{current_time.hour}:{current_time.minute:02d}", f"{current_time.hour}:{current_time.minute:02d}",
font=Font3, font=Font3,
fill=(255, 255, 255), fill=(255, 255, 255),
# fill=cfg["color_theme"]["text"],
) )
draw.text( draw.text(
(20, 152), (25, 152),
current_time.strftime("%a %d"), current_time.strftime("%a %d"),
font=Font2, font=Font2,
fill=(255, 255, 255), fill=(255, 255, 255),
# fill=cfg["color_theme"]["text"], )
draw.text(
(20, 175),
f"CPU: {get_cpu_temp():.2f}°",
font=Font0,
fill=(255, 255, 255),
) )
if current_mode != 0: if current_mode != 0:
current_mode = 0 current_mode = 0
@ -105,7 +125,7 @@ def display_loop(api: Api, cfg: Dict):
current_mode = 1 current_mode = 1
current_track = data["track"] + data["artists"][0] + data["album"] current_track = data["track"] + data["artists"][0] + data["album"]
if current_track != last_track: if current_track != last_track:
print("updating track") print(f"Updating track to : {data['track']} - {data['artists'][0]}")
last_track = current_track last_track = current_track
img = None img = None
try: try:
@ -124,7 +144,7 @@ def display_loop(api: Api, cfg: Dict):
bg.paste(img, (10, 30)) bg.paste(img, (10, 30))
draw = ImageDraw.Draw(bg) draw = ImageDraw.Draw(bg)
draw.text( draw.text(
(145, 33), (145, 34),
"\n".join(textwrap.wrap(", ".join(data["artists"]), width=16)), "\n".join(textwrap.wrap(", ".join(data["artists"]), width=16)),
font=Font1, font=Font1,
fill=cfg["color_theme"]["text"], fill=cfg["color_theme"]["text"],
@ -137,41 +157,41 @@ def display_loop(api: Api, cfg: Dict):
) )
w, h = bg.size w, h = bg.size
w -= 95 w -= 90
h += 4
progress_time = min( progress_time = min(
data["duration_ms"], data["duration_ms"],
data["progress_ms"] + int((time.time() - last_api_call) * 1000), data["progress_ms"] + int((time.time() - last_api_call) * 1000),
) )
progress = min(1, progress_time / data["duration_ms"]) progress = min(1, progress_time / data["duration_ms"])
bar_width = int(w * progress) bar_width = int((w - 10) * progress)
draw.rectangle( draw.rectangle(
[(7, h - 23), (w + 3, h - 7)], [(9, h - 23), (w + 3, h - 7)],
# outline=cfg["color_theme"]["text"], outline=cfg["color_theme"]["text"],
outline=cfg["color_theme"]["bar_outline"], )
draw.rectangle(
[(10, h - 22), (w + 2, h - 8)],
outline=cfg["color_theme"]["text"],
) )
draw.rectangle( draw.rectangle(
[(8, h - 22), (w + 2, h - 8)], [(12, h - 20), (w, h - 10)], fill=cfg["color_theme"]["background"]
# outline=cfg["color_theme"]["text"],
outline=cfg["color_theme"]["bar_outline"],
) )
draw.rectangle( draw.rectangle(
[(10, h - 20), (w, h - 10)], fill=cfg["color_theme"]["background"] [(12, h - 20), (12 + bar_width, h - 10)],
)
draw.rectangle(
[(10, h - 20), (bar_width, h - 10)],
fill=cfg["color_theme"]["bar_inside"], fill=cfg["color_theme"]["bar_inside"],
) )
f_current_time = "{:02.0f}:{:02.0f}".format( f_current_time = "{}:{:02.0f}".format(
*divmod(progress_time // 1000, 60) *divmod(progress_time // 1000, 60)
) )
f_total_time = "{:02d}:{:02d}".format( f_total_time = "{}:{:02d}".format(
*divmod(data["duration_ms"] // 1000, 60) *divmod(data["duration_ms"] // 1000, 60)
) )
draw.rectangle( draw.rectangle(
[(231, 215), (320, 235)], fill=cfg["color_theme"]["background"] [(237, 218), (320, 238)], fill=cfg["color_theme"]["background"]
) )
draw.text( draw.text(
(232, 220), (238, 223),
f"{f_current_time}/{f_total_time}", f"{f_current_time}/{f_total_time}",
font=Font0, font=Font0,
fill=cfg["color_theme"]["text"], fill=cfg["color_theme"]["text"],

View File

@ -19,7 +19,7 @@ class TestApp(unittest.TestCase):
# Check color_theme structure # Check color_theme structure
color_theme = self.cfg["color_theme"] color_theme = self.cfg["color_theme"]
self.assertIsInstance(color_theme, dict) self.assertIsInstance(color_theme, dict)
for key in ["text", "bar_outline", "bar_inside", "background"]: for key in ["text", "bar_inside", "background"]:
self.assertIn(key, color_theme) self.assertIn(key, color_theme)
self.assertIsInstance(color_theme[key], tuple) self.assertIsInstance(color_theme[key], tuple)
self.assertEqual(len(color_theme[key]), 3) self.assertEqual(len(color_theme[key]), 3)