This commit is contained in:
barry 2025-10-01 19:11:46 +02:00
parent 2610d484e8
commit b7e2706b12
6 changed files with 163 additions and 24 deletions

106
poetry.lock generated
View File

@ -1,5 +1,25 @@
# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. # This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand.
[[package]]
name = "anyio"
version = "4.11.0"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"},
{file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"},
]
[package.dependencies]
idna = ">=2.8"
sniffio = ">=1.1"
typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
trio = ["trio (>=0.31.0)"]
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2024.12.14" version = "2024.12.14"
@ -155,6 +175,65 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
typing = ["typing-extensions (>=4.12.2)"] typing = ["typing-extensions (>=4.12.2)"]
[[package]]
name = "h11"
version = "0.16.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
]
[[package]]
name = "httpcore"
version = "1.0.9"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
{file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
]
[package.dependencies]
certifi = "*"
h11 = ">=0.16"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<1.0)"]
[[package]]
name = "httpx"
version = "0.28.1"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
]
[package.dependencies]
anyio = "*"
certifi = "*"
httpcore = "==1.*"
idna = "*"
[package.extras]
brotli = ["brotli", "brotlicffi"]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]] [[package]]
name = "identify" name = "identify"
version = "2.6.5" version = "2.6.5"
@ -531,6 +610,18 @@ files = [
{file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"}, {file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"},
] ]
[[package]]
name = "sniffio"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
[[package]] [[package]]
name = "spidev" name = "spidev"
version = "3.6" version = "3.6"
@ -543,6 +634,19 @@ files = [
{file = "spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b"}, {file = "spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b"},
] ]
[[package]]
name = "typing-extensions"
version = "4.15.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
groups = ["main"]
markers = "python_version < \"3.13\""
files = [
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
]
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "2.3.0" version = "2.3.0"
@ -585,4 +689,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "135cf9001d97689d659830abaa919636d20e76a494585757f9b59fbe9ca3adb5" content-hash = "3e4bb3f59bdb90eac1180a026c9be37147b636f91f1f09cce2d7889b4c517f5e"

View File

@ -13,6 +13,7 @@ numpy = "^2.2.1"
pillow = "^11.1.0" pillow = "^11.1.0"
pyyaml = "^6.0.2" pyyaml = "^6.0.2"
psutil = "^6.1.1" psutil = "^6.1.1"
httpx = "^0.28.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]

View File

@ -1,3 +1,6 @@
anyio==4.11.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc \
--hash=sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4
certifi==2024.12.14 ; python_version >= "3.11" and python_version < "4.0" \ certifi==2024.12.14 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \ --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \
--hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db
@ -94,6 +97,15 @@ charset-normalizer==3.4.1 ; python_version >= "3.11" and python_version < "4.0"
--hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \
--hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \
--hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616
h11==0.16.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
--hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
httpcore==1.0.9 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
--hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
httpx==0.28.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
idna==3.10 ; python_version >= "3.11" and python_version < "4.0" \ idna==3.10 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
@ -300,9 +312,15 @@ pyyaml==6.0.2 ; python_version >= "3.11" and python_version < "4.0" \
requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" \ requests==2.32.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
spidev==3.6 ; python_version >= "3.11" and python_version < "4.0" \ spidev==3.6 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b \ --hash=sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b \
--hash=sha256:280abc00a1ef7780ef62c3f294f52a2527b6c47d8c269fea98664970bcaf6da5 --hash=sha256:280abc00a1ef7780ef62c3f294f52a2527b6c47d8c269fea98664970bcaf6da5
typing-extensions==4.15.0 ; python_version >= "3.11" and python_version < "3.13" \
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
urllib3==2.3.0 ; python_version >= "3.11" and python_version < "4.0" \ urllib3==2.3.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \
--hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d

View File

@ -1,5 +1,5 @@
import requests
import warnings import warnings
import httpx
class Api: class Api:
@ -8,23 +8,27 @@ class Api:
self.base_64 = base_64 self.base_64 = base_64
self.access_token = None self.access_token = None
self.header = None self.header = None
self._client = httpx.AsyncClient(timeout=httpx.Timeout(5.0, connect=5.0))
def refreshAuth(self) -> None: async def refreshAuth(self) -> None:
uri = "https://accounts.spotify.com/api/token" uri = "https://accounts.spotify.com/api/token"
data = { data = {
"grant_type": "refresh_token", "grant_type": "refresh_token",
"refresh_token": self.refresh_token, "refresh_token": self.refresh_token,
} }
req = requests.post( res = await self._client.post(
uri, data=data, headers={"Authorization": "Basic " + self.base_64} uri,
).json() data=data,
headers={"Authorization": "Basic " + self.base_64},
)
req = res.json()
self.access_token = req["access_token"] self.access_token = req["access_token"]
self.header = {"Authorization": f"Bearer {self.access_token}"} self.header = {"Authorization": f"Bearer {self.access_token}"}
return req["expires_in"] return req["expires_in"]
def getPlaying(self): async def getPlaying(self):
url = "https://api.spotify.com/v1/me/player/currently-playing" url = "https://api.spotify.com/v1/me/player/currently-playing"
req = requests.get(url, headers=self.header) req = await self._client.get(url, headers=self.header)
if req.status_code == 204: if req.status_code == 204:
return "not-playing" return "not-playing"
if req.status_code == 401: if req.status_code == 401:

View File

@ -56,10 +56,14 @@ class RaspberryPi:
# self.GPIO.cleanup() # self.GPIO.cleanup()
self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False) self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) # self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) # self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BL_PIN, self.GPIO.OUT) # self.GPIO.setup(self.BL_PIN, self.GPIO.OUT)
self.GPIO.output(self.BL_PIN, self.GPIO.HIGH) # self.GPIO.output(self.BL_PIN, self.GPIO.HIGH)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT, initial=self.GPIO.HIGH)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT, initial=self.GPIO.LOW)
self.GPIO.setup(self.BL_PIN, self.GPIO.OUT, initial=self.GPIO.HIGH)
# Initialize SPI # Initialize SPI
self.SPI = spidev.SpiDev() self.SPI = spidev.SpiDev()
self.SPI.open(0, 0) self.SPI.open(0, 0)
@ -86,9 +90,13 @@ class RaspberryPi:
self._pwm.ChangeFrequency(freq) self._pwm.ChangeFrequency(freq)
def module_init(self): def module_init(self):
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) # self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) # self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BL_PIN, self.GPIO.OUT) # self.GPIO.setup(self.BL_PIN, self.GPIO.OUT)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT, initial=self.GPIO.HIGH)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT, initial=self.GPIO.LOW)
self.GPIO.setup(self.BL_PIN, self.GPIO.OUT, initial=self.GPIO.HIGH)
self._pwm = self.GPIO.PWM(self.BL_PIN, self.BL_freq) self._pwm = self.GPIO.PWM(self.BL_PIN, self.BL_freq)
self._pwm.start(100) self._pwm.start(100)
if self.SPI != None: if self.SPI != None:

View File

@ -9,6 +9,7 @@ from datetime import datetime
import yaml import yaml
from typing import Dict from typing import Dict
import subprocess import subprocess
import asyncio
import numpy as np import numpy as np
import psutil import psutil
@ -62,7 +63,7 @@ def parse_config(path="spotiplayer_pi/config.yaml"):
return data return data
def display_loop(api: Api, cfg: Dict): async def display_loop(api: Api, cfg: Dict):
try: try:
d = LCD_2inch() d = LCD_2inch()
d.Init() d.Init()
@ -98,12 +99,12 @@ def display_loop(api: Api, cfg: Dict):
while True: while True:
if time.time() - last_auth_refresh >= auth_interval: if time.time() - last_auth_refresh >= auth_interval:
auth_interval = api.refreshAuth() - 120 auth_interval = await api.refreshAuth() - 120
last_auth_refresh = time.time() last_auth_refresh = time.time()
print(f"Refreshed auth at {datetime.now().strftime('%d-%m %H:%M:%S')}") print(f"Refreshed auth at {datetime.now().strftime('%d-%m %H:%M:%S')}")
if time.time() - last_api_call >= cfg["api_interval"]: if time.time() - last_api_call >= cfg["api_interval"]:
data = api.getPlaying() data = await api.getPlaying()
last_api_call = time.time() last_api_call = time.time()
if data == None: if data == None:
warnings.warn("No data found") warnings.warn("No data found")
@ -129,7 +130,10 @@ def display_loop(api: Api, cfg: Dict):
idx = cpu_values_idxs[i] idx = cpu_values_idxs[i]
val, prev_val = cpu_values[i], cpu_values[i - 1] val, prev_val = cpu_values[i], cpu_values[i - 1]
draw.rectangle( draw.rectangle(
[(idx, 200 - val), (idx - 1, 200 - prev_val)], [
(min(idx - 1, idx), min(200 - prev_val, 200 - val)),
(max(idx - 1, idx), max(200 - prev_val, 200 - val)),
],
fill=(255, 253, 195), fill=(255, 253, 195),
) )
draw.text( draw.text(
@ -185,8 +189,8 @@ def display_loop(api: Api, cfg: Dict):
idx = len(binned_data) - idx_ - 1 idx = len(binned_data) - idx_ - 1
draw.rectangle( draw.rectangle(
[ [
(321 - ((idx + 1) * 16), 240), (321 - ((idx + 1) * 16), min(240, 240 - bin_i // 65)),
(319 - (idx * 16), 240 - bin_i // 65), (319 - (idx * 16), max(240, 240 - bin_i // 65)),
], ],
fill=tuple(cmap2[idx]), fill=tuple(cmap2[idx]),
) )
@ -293,10 +297,10 @@ def display_loop(api: Api, cfg: Dict):
fill=cfg["color_theme"]["text"], fill=cfg["color_theme"]["text"],
) )
d.ShowImage(bg) d.ShowImage(bg)
time.sleep(cfg["refresh_interval"]) await asyncio.sleep(cfg["refresh_interval"])
except IOError as e: except IOError as e:
raise e print(f"IOError: {e}")
except KeyboardInterrupt: except KeyboardInterrupt:
d.module_exit() d.module_exit()
@ -304,4 +308,4 @@ def display_loop(api: Api, cfg: Dict):
if __name__ == "__main__": if __name__ == "__main__":
cfg = parse_config() cfg = parse_config()
api = Api(cfg["refresh_token"], cfg["base_64"]) api = Api(cfg["refresh_token"], cfg["base_64"])
display_loop(api, cfg) asyncio.run(display_loop(api, cfg))