sync
This commit is contained in:
36
tool/core/cmd/crc32.py
Normal file
36
tool/core/cmd/crc32.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# tool/core/cmd/crc32.py
|
||||
import struct
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS, ERRORS
|
||||
|
||||
class crc32:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, path: str):
|
||||
path_bytes = path.encode('utf-8')
|
||||
payload = struct.pack('B', len(path_bytes)) + path_bytes
|
||||
self.bus.send_request(COMMANDS['crc_32'], payload)
|
||||
|
||||
# 1 Byte Type + 4 Byte Size = 5
|
||||
data = self.bus.receive_response(length=8, timeout=5)
|
||||
|
||||
if not data or data.get('type') == 'error':
|
||||
return None
|
||||
|
||||
payload = data['data']
|
||||
crc_value = struct.unpack('<I', payload[0:4])[0]
|
||||
audio_crc_value = struct.unpack('<I', payload[4:8])[0]
|
||||
result = {
|
||||
'crc32': crc_value,
|
||||
'audio_crc32': audio_crc_value
|
||||
}
|
||||
return result
|
||||
|
||||
def print(self, result, path: str):
|
||||
if not result:
|
||||
return
|
||||
|
||||
console.print(f"[info_title]CRC32[/info_title] für [info]{path}[/info]:")
|
||||
console.print(f" • CRC32 Datei: [info]{result['crc32']:08X}[/info]")
|
||||
console.print(f" • CRC32 Audio: [info]{result['audio_crc32']:08X}[/info]")
|
||||
23
tool/core/cmd/fw_confirm.py
Normal file
23
tool/core/cmd/fw_confirm.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class fw_confirm:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self):
|
||||
# Fehler 1: Der Key in COMMANDS heißt 'confirm_fw'
|
||||
self.bus.send_request(COMMANDS['confirm_fw'])
|
||||
|
||||
# Fehler 2: Try-Except entfernt, damit der ControllerError (z.B. bei nicht-pending Image)
|
||||
# sauber nach buzz.py durchschlägt.
|
||||
data = self.bus.receive_ack()
|
||||
return data is not None and data.get('type') == 'ack'
|
||||
|
||||
def print(self, result):
|
||||
if result:
|
||||
console.print("✓ Laufende Firmware wurde [info]erfolgreich bestätigt[/info] (Permanent).")
|
||||
else:
|
||||
# Wird im Fehlerfall eigentlich nicht mehr erreicht, da buzz.py abbricht,
|
||||
# bleibt aber als Fallback für leere Antworten.
|
||||
console_err.print("❌ Fehler beim Bestätigen der Firmware.")
|
||||
@@ -2,6 +2,7 @@
|
||||
import struct
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
@@ -21,24 +22,34 @@ class get_file:
|
||||
|
||||
source_path_bytes = source_path.encode('utf-8')
|
||||
payload = struct.pack('B', len(source_path_bytes)) + source_path_bytes
|
||||
device_file_crc = None
|
||||
try:
|
||||
self.bus.send_request(COMMANDS['crc_32'], payload)
|
||||
crc_resp = self.bus.receive_response(length=4)
|
||||
if crc_resp and crc_resp.get('type') == 'response':
|
||||
device_file_crc = struct.unpack('<I', crc_resp['data'])[0]
|
||||
except Exception:
|
||||
device_file_crc = None
|
||||
|
||||
self.bus.send_request(COMMANDS['get_file'], payload)
|
||||
|
||||
stream_res = self.bus.receive_stream()
|
||||
|
||||
# Fortschrittsbalken Setup
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
DownloadColumn(),
|
||||
TransferSpeedColumn(),
|
||||
"•",
|
||||
TimeRemainingColumn(),
|
||||
console=console,
|
||||
transient=False
|
||||
) as progress:
|
||||
|
||||
task = progress.add_task(f"Lade {source_path}...", total=None)
|
||||
|
||||
def update_bar(received, total):
|
||||
progress.update(task, total=total, completed=received)
|
||||
|
||||
stream_res = self.bus.receive_stream(progress_callback=update_bar)
|
||||
|
||||
if not stream_res or stream_res.get('type') == 'error':
|
||||
return None
|
||||
|
||||
file_data = stream_res['data']
|
||||
remote_crc = stream_res['crc32']
|
||||
remote_crc = stream_res.get('crc32')
|
||||
local_crc = zlib.crc32(file_data) & 0xFFFFFFFF
|
||||
duratuion = stream_res.get('duration')
|
||||
|
||||
if local_crc == remote_crc:
|
||||
with open(p, 'wb') as f:
|
||||
@@ -55,8 +66,8 @@ class get_file:
|
||||
'dest_path': dest_path,
|
||||
'crc32_remote': remote_crc,
|
||||
'crc32_local': local_crc,
|
||||
'crc32_device_file': device_file_crc,
|
||||
'size': len(file_data)
|
||||
'size': len(file_data),
|
||||
'duration': duratuion
|
||||
}
|
||||
|
||||
def print(self, result):
|
||||
@@ -73,4 +84,6 @@ class get_file:
|
||||
console.print(f" • Local CRC: [info]{result['crc32_local']:08X}[/info]")
|
||||
if result.get('crc32_device_file') is not None:
|
||||
console.print(f" • Device CRC: [info]{result['crc32_device_file']:08X}[/info]")
|
||||
console.print(f" • Zielpfad: [info]{result['dest_path']}[/info]")
|
||||
console.print(f" • Zielpfad: [info]{result['dest_path']}[/info]")
|
||||
if result.get('duration') is not None and result.get('duration') > 0:
|
||||
console.print(f" • Dauer: [info]{result['duration']:.2f} s[/info]")
|
||||
37
tool/core/cmd/get_setting.py
Normal file
37
tool/core/cmd/get_setting.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import struct
|
||||
from core.utils import console
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class get_setting:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, key: str):
|
||||
key_bytes = key.encode('utf-8')
|
||||
payload = struct.pack('B', len(key_bytes)) + key_bytes
|
||||
|
||||
self.bus.send_request(COMMANDS['get_setting'], payload)
|
||||
|
||||
# varlen_params=1 liest exakt 1 Byte Länge + entsprechend viele Datenbytes
|
||||
data = self.bus.receive_response(length=0, varlen_params=1)
|
||||
|
||||
if not data or data.get('type') == 'error':
|
||||
return None
|
||||
|
||||
raw = data['data']
|
||||
val_len = raw[0]
|
||||
val_buf = raw[1:1+val_len]
|
||||
|
||||
# Binärdaten zurück in Python-Typen parsen
|
||||
if key == "audio/vol" and val_len == 1:
|
||||
return struct.unpack('<B', val_buf)[0]
|
||||
elif key == "play/norepeat" and val_len == 1:
|
||||
return bool(struct.unpack('<B', val_buf)[0])
|
||||
elif key == "settings/storage_interval" and val_len == 2:
|
||||
return struct.unpack('<H', val_buf)[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def print(self, result, key: str):
|
||||
if result is not None:
|
||||
console.print(f"⚙️ [info]{key}[/info] = [info]{result}[/info]")
|
||||
43
tool/core/cmd/get_tags.py
Normal file
43
tool/core/cmd/get_tags.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# tool/core/cmd/get_tags.py
|
||||
import struct
|
||||
import json
|
||||
from core.utils import console
|
||||
from core.protocol import COMMANDS
|
||||
from core.tag import TagManager
|
||||
|
||||
class get_tags:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get_raw_tlvs(self, path: str):
|
||||
"""Holt die rohen TLVs vom Gerät."""
|
||||
path_bytes = path.encode('utf-8')
|
||||
payload = struct.pack('B', len(path_bytes)) + path_bytes
|
||||
self.bus.send_request(COMMANDS['get_tags'], payload)
|
||||
|
||||
stream_res = self.bus.receive_stream()
|
||||
if not stream_res or stream_res.get('type') == 'error': return []
|
||||
|
||||
return TagManager.parse_tlvs(stream_res['data'])
|
||||
|
||||
def get(self, path: str):
|
||||
tlvs = self.get_raw_tlvs(path)
|
||||
result = {"system": {}, "json": {}}
|
||||
|
||||
for tlv in tlvs:
|
||||
if tlv['type'] == 0x00:
|
||||
if tlv['index'] == 0x00 and len(tlv['value']) == 8:
|
||||
codec, bit_depth, _, samplerate = struct.unpack('<BBHI', tlv['value'])
|
||||
result["system"]["format"] = {"codec": codec, "bit_depth": bit_depth, "samplerate": samplerate}
|
||||
elif tlv['index'] == 0x01 and len(tlv['value']) == 4:
|
||||
result["system"]["crc32"] = f"0x{struct.unpack('<I', tlv['value'])[0]:08X}"
|
||||
elif tlv['type'] == 0x10:
|
||||
try:
|
||||
result["json"].update(json.loads(tlv['value'].decode('utf-8')))
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
def print(self, result, path: str):
|
||||
console.print(f"[info]Metadaten[/info] für [info]{path}[/info]:")
|
||||
console.print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
23
tool/core/cmd/play.py
Normal file
23
tool/core/cmd/play.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import struct
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class play:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, path: str, interrupt: bool):
|
||||
flags = 0x01 if interrupt else 0x00
|
||||
path_bytes = path.encode('utf-8')
|
||||
|
||||
# Payload: [1 Byte Flags] + [1 Byte Path Length] + [Path String]
|
||||
payload = struct.pack('B', flags) + struct.pack('B', len(path_bytes)) + path_bytes
|
||||
|
||||
self.bus.send_request(COMMANDS['play'], payload)
|
||||
data = self.bus.receive_ack()
|
||||
return data is not None and data.get('type') == 'ack'
|
||||
|
||||
def print(self, result, path: str, interrupt: bool):
|
||||
if result:
|
||||
mode = "sofort (Interrupt)" if interrupt else "in die Warteschlange (Queue)"
|
||||
console.print(f"▶ Wiedergabe von [info]{path}[/info] {mode} eingereiht.")
|
||||
100
tool/core/cmd/put_file.py
Normal file
100
tool/core/cmd/put_file.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# tool/core/cmd/put_file.py
|
||||
import struct
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
from core.tag import TagManager
|
||||
from core.cmd.put_tags import put_tags
|
||||
|
||||
class put_file:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, source_path: str, dest_path: str, cli_tags_json: str = None):
|
||||
try:
|
||||
p = Path(source_path)
|
||||
if not p.exists() or not p.is_file():
|
||||
console_err.print(f"Fehler: Quelldatei existiert nicht: {source_path}")
|
||||
return None
|
||||
with open(p, 'rb') as f:
|
||||
file_data = f.read()
|
||||
except Exception as e:
|
||||
console_err.print(f"Fehler beim Lesen: {e}")
|
||||
return None
|
||||
|
||||
# 1. Lokale Tags abtrennen
|
||||
audio_data, local_tlvs = TagManager.split_file(file_data)
|
||||
audio_size = len(audio_data)
|
||||
|
||||
# 2. Upload der REINEN Audiodaten
|
||||
dest_path_bytes = dest_path.encode('utf-8')
|
||||
payload = struct.pack('B', len(dest_path_bytes)) + dest_path_bytes + struct.pack('<I', audio_size)
|
||||
|
||||
self.bus.send_request(COMMANDS['put_file'], payload)
|
||||
self.bus.receive_ack(timeout=5.0)
|
||||
|
||||
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), BarColumn(), DownloadColumn(), TransferSpeedColumn(), "•", TimeRemainingColumn(), console=console, transient=False) as progress:
|
||||
task = progress.add_task(f"Sende {source_path}...", total=audio_size)
|
||||
stream_res = self.bus.send_stream(audio_data, progress_callback=lambda sent, total: progress.update(task, total=total, completed=sent))
|
||||
|
||||
if not stream_res: return None
|
||||
|
||||
remote_crc = stream_res.get('crc32')
|
||||
local_crc = zlib.crc32(audio_data) & 0xFFFFFFFF
|
||||
|
||||
if local_crc != remote_crc:
|
||||
return {'success': False, 'source_path': source_path, 'crc32_remote': remote_crc, 'crc32_local': local_crc}
|
||||
|
||||
# 3. Tags aktualisieren (CRC32 + evtl. CLI-Tags)
|
||||
# Alten CRC-Tag entfernen, neuen einsetzen
|
||||
final_tlvs = [t for t in local_tlvs if not (t['type'] == 0x00 and t['index'] == 0x01)]
|
||||
final_tlvs.append({'type': 0x00, 'index': 0x01, 'value': struct.pack('<I', local_crc)})
|
||||
|
||||
# Falls CLI-Tags übergeben wurden (-t), diese priorisiert anwenden
|
||||
if cli_tags_json:
|
||||
try:
|
||||
cli_tlvs = TagManager.parse_cli_json(cli_tags_json)
|
||||
# Bestehendes JSON löschen, wenn neues im CLI-Input definiert ist
|
||||
if any(t['type'] == 0x10 for t in cli_tlvs):
|
||||
final_tlvs = [t for t in final_tlvs if t['type'] != 0x10]
|
||||
final_tlvs.extend(cli_tlvs)
|
||||
except ValueError as e:
|
||||
console_err.print(f"[warning]Warnung: Tags konnten nicht geparst werden ({e}). Datei wurde ohne extra Tags hochgeladen.[/warning]")
|
||||
|
||||
# 4. Tags via separatem Befehl anhängen
|
||||
tag_cmd = put_tags(self.bus)
|
||||
tag_blob = TagManager.build_blob(final_tlvs)
|
||||
tag_cmd.send_blob(dest_path, tag_blob)
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'source_path': source_path,
|
||||
'dest_path': dest_path,
|
||||
'crc32_remote': remote_crc,
|
||||
'crc32_local': local_crc,
|
||||
'size': audio_size,
|
||||
'duration': stream_res.get('duration')
|
||||
}
|
||||
|
||||
def print(self, result):
|
||||
if not result:
|
||||
return
|
||||
|
||||
if result.get('success'):
|
||||
console.print(f"✓ Datei [info]{result['source_path']}[/info] erfolgreich hochgeladen und Tags generiert.")
|
||||
console.print(f" • Größe: [info]{result['size'] / 1024:.2f} KB[/info]")
|
||||
else:
|
||||
console_err.print(f"❌ CRC-FEHLER: Datei [error]{result['source_path']}[/error] wurde auf dem Gerät korrumpiert!")
|
||||
|
||||
if 'crc32_remote' in result:
|
||||
console.print(f" • Remote CRC: [info]{result['crc32_remote']:08X}[/info]")
|
||||
if 'crc32_local' in result:
|
||||
console.print(f" • Local CRC: [info]{result['crc32_local']:08X}[/info]")
|
||||
|
||||
if 'dest_path' in result:
|
||||
console.print(f" • Zielpfad: [info]{result['dest_path']}[/info]")
|
||||
|
||||
if result.get('duration') is not None and result.get('duration') > 0:
|
||||
console.print(f" • Dauer: [info]{result['duration']:.2f} s[/info]")
|
||||
89
tool/core/cmd/put_fw.py
Normal file
89
tool/core/cmd/put_fw.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import struct
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class put_fw:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, file_path: str):
|
||||
try:
|
||||
p = Path(file_path)
|
||||
if not p.exists() or not p.is_file():
|
||||
console_err.print(f"Fehler: Firmware-Datei existiert nicht: {file_path}")
|
||||
return None
|
||||
|
||||
file_size = p.stat().st_size
|
||||
with open(p, 'rb') as f:
|
||||
file_data = f.read()
|
||||
except Exception as e:
|
||||
console_err.print(f"Lese-Fehler: {e}")
|
||||
return None
|
||||
|
||||
# 1. Schritt: Löschvorgang mit minimalem Feedback
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
console=console,
|
||||
transient=True
|
||||
) as progress:
|
||||
erase_task = progress.add_task("Lösche Firmware Slot...", total=None)
|
||||
|
||||
payload = struct.pack('<I', file_size)
|
||||
self.bus.send_request(COMMANDS['put_fw'], payload)
|
||||
|
||||
# Warten auf ACK (Balken pulsiert ohne Byte-Anzeige)
|
||||
self.bus.receive_ack(timeout=10.0)
|
||||
progress.update(erase_task, description="✓ Slot gelöscht", completed=100, total=100)
|
||||
|
||||
# 2. Schritt: Eigentlicher Transfer mit allen Metriken (Bytes, Speed, Time)
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
DownloadColumn(),
|
||||
TransferSpeedColumn(),
|
||||
"•",
|
||||
TimeRemainingColumn(),
|
||||
console=console,
|
||||
transient=False
|
||||
) as progress:
|
||||
transfer_task = progress.add_task("Sende Firmware...", total=file_size)
|
||||
|
||||
stream_res = self.bus.send_stream(
|
||||
file_data,
|
||||
progress_callback=lambda sent, total: progress.update(transfer_task, total=total, completed=sent)
|
||||
)
|
||||
|
||||
if not stream_res:
|
||||
return None
|
||||
|
||||
remote_crc = stream_res.get('crc32')
|
||||
local_crc = zlib.crc32(file_data) & 0xFFFFFFFF
|
||||
|
||||
return {
|
||||
'success': local_crc == remote_crc,
|
||||
'source_path': file_path,
|
||||
'crc32_remote': remote_crc,
|
||||
'crc32_local': local_crc,
|
||||
'size': file_size,
|
||||
'duration': stream_res.get('duration')
|
||||
}
|
||||
|
||||
def print(self, result):
|
||||
if not result:
|
||||
return
|
||||
|
||||
if result['success']:
|
||||
console.print(f"✓ Firmware [info]{result['source_path']}[/info] erfolgreich in Slot 1 geschrieben.")
|
||||
console.print(" [warning]Achtung:[/warning] Das Image ist als 'Pending' markiert. Führe 'reboot' aus, um das Update zu installieren.")
|
||||
else:
|
||||
console_err.print(f"❌ CRC-FEHLER: Die Firmware wurde korrumpiert übertragen!")
|
||||
|
||||
console.print(f" • Größe: [info]{result['size'] / 1024:.2f} KB[/info]")
|
||||
console.print(f" • Remote CRC: [info]{result['crc32_remote']:08X}[/info]")
|
||||
console.print(f" • Local CRC: [info]{result['crc32_local']:08X}[/info]")
|
||||
68
tool/core/cmd/put_tags.py
Normal file
68
tool/core/cmd/put_tags.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# tool/core/cmd/put_tags.py
|
||||
import struct
|
||||
import json
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
from core.tag import TagManager
|
||||
from core.cmd.get_tags import get_tags
|
||||
|
||||
class put_tags:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, path: str, json_str: str, overwrite: bool):
|
||||
try:
|
||||
new_tlvs = TagManager.parse_cli_json(json_str)
|
||||
except ValueError as e:
|
||||
console_err.print(f"[error]{e}[/error]")
|
||||
return False
|
||||
|
||||
getter = get_tags(self.bus)
|
||||
existing_tlvs = getter.get_raw_tlvs(path)
|
||||
|
||||
if overwrite:
|
||||
# Bei Overwrite: Alle alten JSON-Tags löschen
|
||||
existing_tlvs = [t for t in existing_tlvs if t['type'] != 0x10]
|
||||
else:
|
||||
# Ohne Overwrite: Bestehende JSON-Werte mit neuen mischen
|
||||
existing_json = {}
|
||||
for t in existing_tlvs:
|
||||
if t['type'] == 0x10:
|
||||
try: existing_json.update(json.loads(t['value'].decode('utf-8')))
|
||||
except: pass
|
||||
|
||||
# Neues JSON einmischen
|
||||
for nt in new_tlvs:
|
||||
if nt['type'] == 0x10:
|
||||
try: existing_json.update(json.loads(nt['value'].decode('utf-8')))
|
||||
except: pass
|
||||
|
||||
existing_tlvs = [t for t in existing_tlvs if t['type'] != 0x10]
|
||||
if existing_json:
|
||||
existing_tlvs.append({'type': 0x10, 'index': 0x00, 'value': json.dumps(existing_json, ensure_ascii=False).encode('utf-8')})
|
||||
|
||||
# System-Tags (0x00) überschreiben alte direkt
|
||||
new_sys_tlvs = [t for t in new_tlvs if t['type'] == 0x00]
|
||||
for nt in new_sys_tlvs:
|
||||
existing_tlvs = [t for t in existing_tlvs if not (t['type'] == nt['type'] and t['index'] == nt['index'])]
|
||||
existing_tlvs.append(nt)
|
||||
|
||||
new_tlvs = existing_tlvs
|
||||
|
||||
blob = TagManager.build_blob(new_tlvs)
|
||||
return self.send_blob(path, blob)
|
||||
|
||||
def send_blob(self, path: str, blob: bytes):
|
||||
path_bytes = path.encode('utf-8')
|
||||
req_payload = struct.pack('B', len(path_bytes)) + path_bytes + struct.pack('<I', len(blob))
|
||||
|
||||
self.bus.send_request(COMMANDS['put_tags'], req_payload)
|
||||
self.bus.receive_ack(timeout=2.0)
|
||||
|
||||
if self.bus.send_stream(blob):
|
||||
return True
|
||||
return False
|
||||
|
||||
def print(self, result, path: str):
|
||||
if result:
|
||||
console.print(f"✓ Metadaten erfolgreich auf [info]{path}[/info] geschrieben.")
|
||||
23
tool/core/cmd/reboot.py
Normal file
23
tool/core/cmd/reboot.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import serial
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class reboot:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self):
|
||||
self.bus.send_request(COMMANDS['reboot'])
|
||||
try:
|
||||
data = self.bus.receive_ack()
|
||||
return data is not None and data.get('type') == 'ack'
|
||||
except serial.SerialException:
|
||||
# SerialException MUSS hier ignoriert werden, da der Controller
|
||||
# den USB-Port beim Reboot hart schließt
|
||||
return True
|
||||
|
||||
def print(self, result):
|
||||
if result:
|
||||
console.print("🔄 Neustart-Befehl erfolgreich gesendet. Controller [info]bootet neu...[/info]")
|
||||
else:
|
||||
console_err.print("❌ Fehler beim Senden des Neustart-Befehls.")
|
||||
38
tool/core/cmd/set_setting.py
Normal file
38
tool/core/cmd/set_setting.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import struct
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class set_setting:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self, key: str, value: str):
|
||||
key_bytes = key.encode('utf-8')
|
||||
val_bytes = b''
|
||||
|
||||
# Typen-Konvertierung basierend auf dem Key
|
||||
try:
|
||||
if key == "audio/vol":
|
||||
val_bytes = struct.pack('<B', int(value))
|
||||
elif key == "play/norepeat":
|
||||
val_int = 1 if str(value).lower() in ['1', 'true', 'on', 'yes'] else 0
|
||||
val_bytes = struct.pack('<B', val_int)
|
||||
elif key == "settings/storage_interval":
|
||||
val_bytes = struct.pack('<H', int(value))
|
||||
else:
|
||||
console_err.print(f"[error]Unbekannter Key: {key}[/error]")
|
||||
return False
|
||||
except ValueError:
|
||||
console_err.print(f"[error]Ungültiger Wert für {key}: {value}[/error]")
|
||||
return False
|
||||
|
||||
# Payload: [Key Len] [Key] [Val Len] [Val Bytes]
|
||||
payload = struct.pack('B', len(key_bytes)) + key_bytes + struct.pack('B', len(val_bytes)) + val_bytes
|
||||
self.bus.send_request(COMMANDS['set_setting'], payload)
|
||||
|
||||
data = self.bus.receive_ack()
|
||||
return data is not None and data.get('type') == 'ack'
|
||||
|
||||
def print(self, result, key: str, value: str):
|
||||
if result:
|
||||
console.print(f"✓ Setting [info]{key}[/info] wurde auf [info]{value}[/info] gesetzt.")
|
||||
15
tool/core/cmd/stop.py
Normal file
15
tool/core/cmd/stop.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from core.utils import console, console_err
|
||||
from core.protocol import COMMANDS
|
||||
|
||||
class stop:
|
||||
def __init__(self, bus):
|
||||
self.bus = bus
|
||||
|
||||
def get(self):
|
||||
self.bus.send_request(COMMANDS['stop'])
|
||||
data = self.bus.receive_ack()
|
||||
return data is not None and data.get('type') == 'ack'
|
||||
|
||||
def print(self, result):
|
||||
if result:
|
||||
console.print("⏹ Wiedergabe gestoppt und Warteschlange geleert.")
|
||||
Reference in New Issue
Block a user