One-click and nightly firmware updates from ESP Screens
Screens now report their ESPHome name and IP address, so the add-on can match each screen to its YAML profile and OTA address itself. The screen list shows an update badge with a Bijwerken button, an update-all action and a nightly checkbox; updates run one screen at a time, wait for the new version to report back and stop the round on the first failure. App 0.2.16, firmware 0.2.17. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,10 @@ import time
|
||||
import yaml
|
||||
from core import installation_yaml
|
||||
|
||||
class LenientLoader(yaml.SafeLoader):
|
||||
"""Reads profile metadata without resolving !secret or !include."""
|
||||
LenientLoader.add_multi_constructor('!', lambda loader, suffix, node: None)
|
||||
|
||||
class Firmware:
|
||||
def __init__(self, root, data):
|
||||
self.root, self.data = Path(root).resolve(), Path(data)
|
||||
@@ -25,6 +29,25 @@ class Firmware:
|
||||
return [{'file': p.name} for p in sorted(self.root.glob('*.yaml'))
|
||||
if p.name != 'secrets.yaml' and p.is_file() and not p.is_symlink()]
|
||||
|
||||
def profile_names(self):
|
||||
"""{file: {'node': esphome name, 'friendly': friendly name}} for every readable profile."""
|
||||
found = {}
|
||||
for entry in self.profiles():
|
||||
try:
|
||||
data = yaml.load((self.root / entry['file']).read_text(), Loader=LenientLoader)
|
||||
except (OSError, yaml.YAMLError, UnicodeError):
|
||||
continue
|
||||
block = data.get('esphome') if isinstance(data, dict) else None
|
||||
if not isinstance(block, dict):
|
||||
continue
|
||||
substitutions = data.get('substitutions') if isinstance(data.get('substitutions'), dict) else {}
|
||||
def resolve(value):
|
||||
if isinstance(value, str) and value.startswith('${') and value.endswith('}'):
|
||||
value = substitutions.get(value[2:-1])
|
||||
return value if isinstance(value, str) else None
|
||||
found[entry['file']] = {'node': resolve(block.get('name')), 'friendly': resolve(block.get('friendly_name'))}
|
||||
return found
|
||||
|
||||
def ports(self):
|
||||
return sorted(set(glob.glob('/dev/serial/by-id/*') or glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*')))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user