car_ui/build_info.py
2026-01-09 03:06:15 +03:00

163 lines
4.4 KiB
Python

import os
import platform
import shutil
import socket
APP_NAME = "Car UI"
VERSION = "0.1.0-dev"
BUILD_DATE = "dev"
GIT_HASH = "dev"
DEVICE_MODEL = "Raspberry Pi"
DEFAULT_SOUND_VOLUME = 100
DEFAULT_PREMUTE_VOLUME = 10
DEFAULT_DUCKING_VOLUME = 35
def get_device_model() -> str:
for path in ("/proc/device-tree/model", "/sys/firmware/devicetree/base/model"):
try:
with open(path, "rb") as f:
raw = f.read().strip(b"\x00").strip()
if raw:
return raw.decode("utf-8", errors="replace")
except OSError:
continue
return DEVICE_MODEL
def get_os_pretty_name() -> str:
for path in ("/etc/os-release", "/usr/lib/os-release"):
try:
with open(path, "r", encoding="utf-8") as f:
for line in f:
if line.startswith("PRETTY_NAME="):
value = line.split("=", 1)[1].strip().strip('"')
return value or ""
except OSError:
continue
return platform.system() or ""
def get_kernel_version() -> str:
return platform.release() or ""
def _read_meminfo() -> dict[str, int]:
info: dict[str, int] = {}
try:
with open("/proc/meminfo", "r", encoding="utf-8") as f:
for line in f:
if ":" not in line:
continue
key, rest = line.split(":", 1)
parts = rest.strip().split()
if not parts:
continue
try:
value_kb = int(parts[0])
except ValueError:
continue
info[key] = value_kb * 1024
except OSError:
return {}
return info
def _format_bytes(num_bytes: int) -> str:
if num_bytes <= 0:
return ""
gib = num_bytes / (1024 ** 3)
if gib >= 1:
return f"{gib:.1f} GB"
mib = num_bytes / (1024 ** 2)
return f"{mib:.0f} MB"
def get_ram_info() -> str:
info = _read_meminfo()
total = info.get("MemTotal", 0)
free = info.get("MemFree", 0)
if total <= 0:
return ""
return f"{_format_bytes(free)} / {_format_bytes(total)}"
def get_disk_info() -> str:
try:
usage = shutil.disk_usage("/")
except OSError:
return ""
return f"{_format_bytes(usage.used)} / {_format_bytes(usage.total)}"
def get_serial_number() -> str:
for path in ("/proc/device-tree/serial-number",):
try:
with open(path, "rb") as f:
raw = f.read().strip(b"\x00").strip()
if raw:
return raw.decode("utf-8", errors="replace")
except OSError:
continue
try:
with open("/proc/cpuinfo", "r", encoding="utf-8") as f:
for line in f:
if line.lower().startswith("serial"):
return line.split(":", 1)[1].strip() or ""
except OSError:
pass
return ""
def get_ip_address() -> str:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
sock.connect(("8.8.8.8", 80))
ip = sock.getsockname()[0]
finally:
sock.close()
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
try:
ip = socket.gethostbyname(socket.gethostname())
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
return ""
def get_cpu_temp() -> str:
for path in ("/sys/class/thermal/thermal_zone0/temp",):
try:
with open(path, "r", encoding="utf-8") as f:
raw = f.read().strip()
if raw:
temp_c = float(raw) / 1000.0
return f"{temp_c:.1f} C"
except (OSError, ValueError):
continue
return ""
def get_display_resolution() -> str:
for path in ("/sys/class/graphics/fb0/virtual_size", "/sys/class/graphics/fb0/modes"):
try:
with open(path, "r", encoding="utf-8") as f:
raw = f.read().strip()
if not raw:
continue
if "x" in raw:
first_line = raw.splitlines()[0]
return first_line.split(" ", 1)[0]
if "," in raw:
width, height = raw.split(",", 1)
return f"{width.strip()}x{height.strip()}"
except OSError:
continue
return ""