web ui bug fixes

This commit is contained in:
Chuck
2025-09-15 11:57:52 -04:00
parent 9bc0cd5629
commit 691d39675d
2 changed files with 43 additions and 58 deletions

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env python3
"""
Simple script to update files on the Pi
"""
import subprocess
import sys
def copy_file_to_pi(local_file, remote_path):
"""Copy a file to the Pi using scp"""
try:
cmd = ['scp', local_file, f'ledpi@ledpi:{remote_path}']
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
print(f"Successfully copied {local_file} to {remote_path}")
return True
except subprocess.CalledProcessError as e:
print(f"Error copying {local_file}: {e}")
print(f"stderr: {e.stderr}")
return False
if __name__ == "__main__":
# Copy the updated web interface file
success1 = copy_file_to_pi('web_interface_v2.py', '/home/ledpi/LEDMatrix/')
# Copy the updated template file
success2 = copy_file_to_pi('templates/index_v2.html', '/home/ledpi/LEDMatrix/templates/')
if success1 and success2:
print("All files copied successfully!")
print("You can now restart the web interface on the Pi.")
else:
print("Some files failed to copy. Please check the errors above.")
sys.exit(1)

View File

@@ -64,60 +64,77 @@ logging.basicConfig(level=logging.INFO)
class DictWrapper: class DictWrapper:
"""Wrapper to make dictionary accessible via dot notation for Jinja2 templates.""" """Wrapper to make dictionary accessible via dot notation for Jinja2 templates."""
def __init__(self, data): def __init__(self, data=None):
# Store the original data
object.__setattr__(self, '_data', data if isinstance(data, dict) else {})
# Set attributes from the dictionary
if isinstance(data, dict): if isinstance(data, dict):
for key, value in data.items(): for key, value in data.items():
if isinstance(value, dict): if isinstance(value, dict):
setattr(self, key, DictWrapper(value)) object.__setattr__(self, key, DictWrapper(value))
elif isinstance(value, list):
object.__setattr__(self, key, value)
else: else:
setattr(self, key, value) object.__setattr__(self, key, value)
else:
# If not a dict, just store the value
self._value = data
def __getattr__(self, name): def __getattr__(self, name):
# Return a new DictWrapper with empty dict for missing attributes # Return a new empty DictWrapper for missing attributes
# This allows chaining like main_config.display.hardware.rows # This allows chaining like main_config.display.hardware.rows
return DictWrapper({}) return DictWrapper({})
def __getitem__(self, key): def __getitem__(self, key):
# Support bracket notation as fallback # Support bracket notation
return getattr(self, key, DictWrapper({})) return getattr(self, key, DictWrapper({}))
def items(self): def items(self):
# Support .items() method for iteration # Support .items() method for iteration
if hasattr(self, '_value'): data = object.__getattribute__(self, '_data')
return {} if data:
return {k: v for k, v in self.__dict__.items() if not k.startswith('_')} return data.items()
return {}.items()
def get(self, key, default=None): def get(self, key, default=None):
# Support .get() method # Support .get() method
return getattr(self, key, default) data = object.__getattribute__(self, '_data')
if data and key in data:
value = data[key]
if isinstance(value, dict):
return DictWrapper(value)
return value
return default
def keys(self):
# Support .keys() method
data = object.__getattribute__(self, '_data')
return data.keys() if data else [].keys()
def values(self):
# Support .values() method
data = object.__getattribute__(self, '_data')
return data.values() if data else [].values()
def __str__(self): def __str__(self):
# Return empty string for missing values to avoid template errors # Return empty string for missing values to avoid template errors
if hasattr(self, '_value'):
return str(self._value) if self._value is not None else ''
return '' return ''
def __repr__(self): def __repr__(self):
# Return empty string for missing values # Return empty string for missing values
if hasattr(self, '_value'):
return repr(self._value) if self._value is not None else ''
return '' return ''
def __html__(self): def __html__(self):
# Support for MarkupSafe HTML escaping # Support for MarkupSafe HTML escaping
return str(self) return ''
def __bool__(self): def __bool__(self):
# Return False for empty wrappers # Return False for empty wrappers, True if has data
if hasattr(self, '_value'): data = object.__getattribute__(self, '_data')
# Avoid recursion by checking if _value is a DictWrapper return bool(data)
if isinstance(self._value, DictWrapper):
return False # Empty DictWrapper is falsy def __len__(self):
return bool(self._value) # Support len() function
return False data = object.__getattribute__(self, '_data')
return len(data) if data else 0
class DisplayMonitor: class DisplayMonitor:
def __init__(self): def __init__(self):
@@ -458,7 +475,7 @@ def index():
return render_template('index_v2.html', return render_template('index_v2.html',
schedule_config=schedule_config, schedule_config=schedule_config,
main_config=main_config, main_config=DictWrapper(main_config),
main_config_data=main_config_data, main_config_data=main_config_data,
secrets_config=secrets_config_data, secrets_config=secrets_config_data,
main_config_json=main_config_json, main_config_json=main_config_json,
@@ -475,7 +492,7 @@ def index():
safe_secrets = {'weather': {'api_key': ''}} safe_secrets = {'weather': {'api_key': ''}}
return render_template('index_v2.html', return render_template('index_v2.html',
schedule_config={}, schedule_config={},
main_config={}, main_config=DictWrapper({}),
main_config_data={}, main_config_data={},
secrets_config=safe_secrets, secrets_config=safe_secrets,
main_config_json="{}", main_config_json="{}",