Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
"""
|
|
|
|
|
Centralized configuration singleton for PyBLOCK.
|
|
|
|
|
|
|
|
|
|
Loads all .conf files once at startup and caches them in memory.
|
|
|
|
|
Call cfg.load() once, then access cfg.path, cfg.lndconnectload, etc.
|
|
|
|
|
Call cfg.reload() after the setup wizard writes new config files.
|
2026-04-01 16:30:47 -03:00
|
|
|
|
|
|
|
|
Supports Umbrel/Docker environment variables for auto-configuration:
|
|
|
|
|
BITCOIN_RPC_HOST, BITCOIN_RPC_PORT, BITCOIN_RPC_USER, BITCOIN_RPC_PASS
|
|
|
|
|
BITCOIN_CLI_PATH
|
|
|
|
|
LND_HOST, LND_GRPC_PORT, LND_TLS_CERT_PATH, LND_MACAROON_PATH, LND_CLI_PATH
|
|
|
|
|
PYBLOCK_MODE (A=Bitcoin+Lightning, B=Bitcoin, C=Lite)
|
Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
_DEFAULT_PATH = {"ip_port": "", "rpcuser": "", "rpcpass": "", "bitcoincli": ""}
|
|
|
|
|
_DEFAULT_LND = {"ip_port": "", "tls": "", "macaroon": "", "ln": ""}
|
|
|
|
|
_DEFAULT_SETTINGS = {"gradient": "", "design": "block", "colorA": "green", "colorB": "yellow"}
|
Add enhanced block clock with 12 new features for Menu A
New clock/ package replaces the old design() polling loop with a
flicker-free ANSI cursor-positioned renderer. Features include:
countdown timer, epoch/halving progress bar, fee rate indicator,
hashrate sparkline, matrix mining animation, odometer digit transition,
heartbeat pulse, zen mode, UTC time display, fireworks on milestone
blocks, generative hash art, and configurable sound modes.
All features are toggleable via Settings → D (Clock Display Settings).
Also fixes hardcoded config paths in menuSelection(), menuSelectionLN(),
and TUI startup to use the cfg singleton instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 08:35:59 -03:00
|
|
|
_DEFAULT_SETTINGS_CLOCK = {
|
|
|
|
|
"gradient": "", "colorA": "green", "colorB": "yellow",
|
|
|
|
|
"show_countdown": True, "show_epoch_bar": True,
|
|
|
|
|
"show_fee_rates": True, "show_sparkline": False,
|
|
|
|
|
"show_utc_time": False, "zen_mode": False,
|
|
|
|
|
"animation": "matrix",
|
|
|
|
|
"fireworks": True,
|
|
|
|
|
"generative_art": False,
|
|
|
|
|
"sound": "bell",
|
|
|
|
|
"heartbeat": True,
|
2026-04-02 09:08:18 -03:00
|
|
|
"show_miner_pool": True,
|
|
|
|
|
"show_block_weight": False,
|
|
|
|
|
"show_block_times": True,
|
|
|
|
|
"show_peers": False,
|
|
|
|
|
"show_moon": False,
|
Add enhanced block clock with 12 new features for Menu A
New clock/ package replaces the old design() polling loop with a
flicker-free ANSI cursor-positioned renderer. Features include:
countdown timer, epoch/halving progress bar, fee rate indicator,
hashrate sparkline, matrix mining animation, odometer digit transition,
heartbeat pulse, zen mode, UTC time display, fireworks on milestone
blocks, generative hash art, and configurable sound modes.
All features are toggleable via Settings → D (Clock Display Settings).
Also fixes hardcoded config paths in menuSelection(), menuSelectionLN(),
and TUI startup to use the cfg singleton instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 08:35:59 -03:00
|
|
|
}
|
Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
|
|
|
|
|
|
2026-04-01 16:30:47 -03:00
|
|
|
def _env_bitcoin_config():
|
|
|
|
|
"""Build Bitcoin config from environment variables (Umbrel/Docker)."""
|
|
|
|
|
host = os.environ.get("BITCOIN_RPC_HOST", "")
|
|
|
|
|
port = os.environ.get("BITCOIN_RPC_PORT", "8332")
|
|
|
|
|
user = os.environ.get("BITCOIN_RPC_USER", "")
|
|
|
|
|
passwd = os.environ.get("BITCOIN_RPC_PASS", "")
|
|
|
|
|
cli = os.environ.get("BITCOIN_CLI_PATH", "")
|
|
|
|
|
|
|
|
|
|
if host and user:
|
|
|
|
|
return {
|
|
|
|
|
"ip_port": f"http://{host}:{port}",
|
|
|
|
|
"rpcuser": user,
|
|
|
|
|
"rpcpass": passwd,
|
|
|
|
|
"bitcoincli": cli,
|
|
|
|
|
}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _env_lnd_config():
|
|
|
|
|
"""Build LND config from environment variables (Umbrel/Docker)."""
|
|
|
|
|
host = os.environ.get("LND_HOST", "")
|
|
|
|
|
port = os.environ.get("LND_GRPC_PORT", "10009")
|
|
|
|
|
tls = os.environ.get("LND_TLS_CERT_PATH", "")
|
|
|
|
|
macaroon = os.environ.get("LND_MACAROON_PATH", "")
|
|
|
|
|
cli = os.environ.get("LND_CLI_PATH", "")
|
|
|
|
|
|
|
|
|
|
if host or tls or macaroon:
|
|
|
|
|
return {
|
|
|
|
|
"ip_port": f"{host}:{port}" if host else "",
|
|
|
|
|
"tls": tls,
|
|
|
|
|
"macaroon": macaroon,
|
|
|
|
|
"ln": cli,
|
|
|
|
|
}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _env_mode():
|
|
|
|
|
"""Get PyBLOCK mode from environment variable."""
|
|
|
|
|
return os.environ.get("PYBLOCK_MODE", "")
|
|
|
|
|
|
|
|
|
|
|
Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
class Config:
|
|
|
|
|
_instance = None
|
|
|
|
|
|
|
|
|
|
def __new__(cls):
|
|
|
|
|
if cls._instance is None:
|
|
|
|
|
cls._instance = super().__new__(cls)
|
|
|
|
|
cls._instance._loaded = False
|
|
|
|
|
return cls._instance
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
if not self._loaded:
|
|
|
|
|
self.config_dir = self._find_config_dir()
|
|
|
|
|
self.path = dict(_DEFAULT_PATH)
|
|
|
|
|
self.lndconnectload = dict(_DEFAULT_LND)
|
|
|
|
|
self.settings = dict(_DEFAULT_SETTINGS)
|
|
|
|
|
self.settings_clock = dict(_DEFAULT_SETTINGS_CLOCK)
|
|
|
|
|
self.intro_mode = None
|
|
|
|
|
|
|
|
|
|
def _find_config_dir(self):
|
|
|
|
|
candidates = [
|
|
|
|
|
os.path.join(os.path.dirname(__file__), "config"),
|
|
|
|
|
"config",
|
|
|
|
|
os.path.join(os.path.dirname(__file__), "SPV", "config"),
|
|
|
|
|
]
|
|
|
|
|
for d in candidates:
|
|
|
|
|
if os.path.isdir(d):
|
|
|
|
|
return d
|
|
|
|
|
return "config"
|
|
|
|
|
|
|
|
|
|
def _load_json(self, filename, defaults=None):
|
|
|
|
|
filepath = os.path.join(self.config_dir, filename)
|
|
|
|
|
if os.path.isfile(filepath):
|
|
|
|
|
with open(filepath, "r") as f:
|
|
|
|
|
data = json.load(f)
|
|
|
|
|
if defaults and isinstance(data, dict):
|
|
|
|
|
merged = dict(defaults)
|
|
|
|
|
merged.update(data)
|
|
|
|
|
return merged
|
|
|
|
|
return data
|
|
|
|
|
return dict(defaults) if defaults else None
|
|
|
|
|
|
2026-04-01 16:30:47 -03:00
|
|
|
def _apply_env_overrides(self):
|
|
|
|
|
"""Apply environment variable overrides (Umbrel/Docker mode).
|
|
|
|
|
|
|
|
|
|
Env vars take priority over config files. If env vars are set,
|
|
|
|
|
they also auto-generate the config files for consistency.
|
|
|
|
|
"""
|
|
|
|
|
btc_env = _env_bitcoin_config()
|
|
|
|
|
if btc_env:
|
|
|
|
|
self.path.update(btc_env)
|
|
|
|
|
self._ensure_config("bclock.conf", self.path)
|
|
|
|
|
|
|
|
|
|
lnd_env = _env_lnd_config()
|
|
|
|
|
if lnd_env:
|
|
|
|
|
self.lndconnectload.update(lnd_env)
|
|
|
|
|
self._ensure_config("blndconnect.conf", self.lndconnectload)
|
|
|
|
|
|
|
|
|
|
mode_env = _env_mode()
|
|
|
|
|
if mode_env and mode_env in ("A", "B", "C"):
|
|
|
|
|
self.intro_mode = mode_env
|
|
|
|
|
self._ensure_config("intro.conf", mode_env)
|
|
|
|
|
|
|
|
|
|
def _ensure_config(self, filename, data):
|
|
|
|
|
"""Write config file if it doesn't exist or env vars are set."""
|
|
|
|
|
filepath = os.path.join(self.config_dir, filename)
|
|
|
|
|
os.makedirs(self.config_dir, exist_ok=True)
|
|
|
|
|
with open(filepath, "w") as f:
|
|
|
|
|
json.dump(data, f, indent=2)
|
|
|
|
|
|
Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
def load(self):
|
|
|
|
|
self.path = self._load_json("bclock.conf", _DEFAULT_PATH)
|
|
|
|
|
self.lndconnectload = self._load_json("blndconnect.conf", _DEFAULT_LND)
|
|
|
|
|
self.settings = self._load_json("pyblocksettings.conf", _DEFAULT_SETTINGS)
|
|
|
|
|
self.settings_clock = self._load_json("pyblocksettingsClock.conf", _DEFAULT_SETTINGS_CLOCK)
|
|
|
|
|
self.intro_mode = self._load_json("intro.conf")
|
2026-04-01 16:30:47 -03:00
|
|
|
self._apply_env_overrides()
|
Refactor architecture: config singleton, logging, menu system, dependencies
Major improvements across 7 areas:
- Add centralized Config singleton (config.py) replacing ~176 config
reloads per session with a single cached load
- Add logging framework (log.py) with RotatingFileHandler, replacing
silent except Exception: pass blocks with structured logging
- Refactor menu system (menu.py) with data-driven color selection,
eliminating ~1,370 lines of duplicate menu functions
- Create shared/ modules extracting 7 utility functions duplicated
between PyBlock.py and SPV/spvblock.py
- Clean dependencies: pin all versions, remove stdlib packages
(asyncio, threading), remove unused imports
- Improve Docker: pin ubuntu:24.04, add non-root user, use venv
- Improve CI: update to actions v4/v5, add test job before publish
- Fix entry point: wrap main loop in def main(), proper module import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 10:58:24 -03:00
|
|
|
self._loaded = True
|
|
|
|
|
|
|
|
|
|
def reload(self):
|
|
|
|
|
self._loaded = False
|
|
|
|
|
self.load()
|
|
|
|
|
|
|
|
|
|
def save(self, filename, data):
|
|
|
|
|
filepath = os.path.join(self.config_dir, filename)
|
|
|
|
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
|
|
|
with open(filepath, "w") as f:
|
|
|
|
|
json.dump(data, f, indent=2)
|
|
|
|
|
self.reload()
|
|
|
|
|
|
|
|
|
|
def has_config(self, filename):
|
|
|
|
|
return os.path.isfile(os.path.join(self.config_dir, filename))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cfg = Config()
|