Add Guition 480x480 wallbox UI with GT911 and stable RGB rendering
This commit is contained in:
@@ -7,7 +7,7 @@ import zipfile
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FILES = ['.gitignore', 'README.md', 'AGENTS.md', 'requirements.txt', 'device.example.yaml',
|
||||
'calibration.example.yaml', 'secrets.yaml.example', 'cyd_ui.h', 'LICENSE',
|
||||
'TILE_CONFIGURATION.md', 'CYD_STABILITY.md', 'TEST_RESULTS.md']
|
||||
'guition_diagnostics.h', 'guition-4848s040.yaml', 'guition-device.example.yaml', 'TILE_CONFIGURATION.md', 'CYD_STABILITY.md', 'TEST_RESULTS.md']
|
||||
FOLDERS = ['docs', 'tools', 'tests', 'components', 'diagnostics', 'fonts']
|
||||
SUFFIXES = {'.py', '.cpp', '.h', '.md', '.ttf', '.txt'}
|
||||
|
||||
|
||||
+12
-8
@@ -9,18 +9,20 @@ import secrets
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def create(directory, name, friendly_name):
|
||||
def create(directory, name, friendly_name, board="cyd"):
|
||||
if not re.fullmatch(r'[a-z][a-z0-9-]{0,30}', name) or name.endswith('-'):
|
||||
raise ValueError('Gebruik een unieke naam van 1–31 tekens: kleine letters, cijfers en koppeltekens.')
|
||||
if board not in ("cyd", "guition"):
|
||||
raise ValueError("Onbekend bordprofiel")
|
||||
import json
|
||||
device = (ROOT / 'device.example.yaml').read_text().replace('"cyd-new"', json.dumps(name), 1)
|
||||
device = device.replace('"Mijn CYD"', json.dumps(friendly_name, ensure_ascii=False), 1)
|
||||
device = (ROOT / ('guition-device.example.yaml' if board == 'guition' else 'device.example.yaml')).read_text().replace('"guition-new"' if board == 'guition' else '"cyd-new"', json.dumps(name), 1)
|
||||
device = device.replace('"Mijn Guition"' if board == 'guition' else '"Mijn CYD"', json.dumps(friendly_name, ensure_ascii=False), 1)
|
||||
secret_text = (ROOT / 'secrets.yaml.example').read_text()
|
||||
replacements = [base64.b64encode(secrets.token_bytes(32)).decode(), secrets.token_urlsafe(24), secrets.token_urlsafe(18)]
|
||||
for value in replacements:
|
||||
secret_text = secret_text.replace('WORDT_UNIEK_GEGENEREERD', value, 1)
|
||||
files = {'device.yaml': device, 'calibration.yaml': (ROOT / 'calibration.example.yaml').read_text(),
|
||||
'secrets.yaml': secret_text}
|
||||
files = {'guition-device.yaml' if board == 'guition' else 'device.yaml': device, 'secrets.yaml': secret_text}
|
||||
if board == 'cyd': files['calibration.yaml'] = (ROOT / 'calibration.example.yaml').read_text()
|
||||
directory = Path(directory)
|
||||
if not directory.is_dir():
|
||||
raise ValueError('De doelmap moet al bestaan en de uitgepakte code bevatten.')
|
||||
@@ -44,15 +46,17 @@ def create(directory, name, friendly_name):
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--board', choices=['cyd', 'guition'], default='cyd')
|
||||
parser.add_argument('--name', required=True, help='Unieke ESPHome-naam, bijvoorbeeld display-keuken')
|
||||
parser.add_argument('--friendly-name', default='Mijn CYD')
|
||||
parser.add_argument('--friendly-name')
|
||||
parser.add_argument('--directory', type=Path, default=ROOT)
|
||||
args = parser.parse_args()
|
||||
args.friendly_name = args.friendly_name or ('Mijn Guition' if args.board == 'guition' else 'Mijn CYD')
|
||||
try:
|
||||
for path in create(args.directory, args.name, args.friendly_name):
|
||||
for path in create(args.directory, args.name, args.friendly_name, args.board):
|
||||
print(f'Gemaakt: {path}')
|
||||
print('Vul wifi lokaal in secrets.yaml in. Sleutels zijn uniek gegenereerd en worden niet getoond.')
|
||||
print('Volg nu README.md; flash device.yaml, niet rechtstreeks de basisconfiguratie.')
|
||||
print('Volg docs/GUITION.md; flash guition-device.yaml.' if args.board == 'guition' else 'Volg README.md; flash device.yaml.')
|
||||
except (ValueError, OSError) as exc:
|
||||
parser.exit(1, f'Fout: {exc}\n')
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Guided USB pixel verification for GT911, without resistive calibration or HA actions."""
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import re
|
||||
import statistics
|
||||
import time
|
||||
from calibrate import write_file
|
||||
|
||||
TARGETS = [('linksboven', (24, 24)), ('rechtsboven', (455, 24)),
|
||||
('rechtsonder', (455, 455)), ('linksonder', (24, 455)), ('midden', (240, 240))]
|
||||
PATTERN = re.compile(r'GT911 press x=(\d+) y=(\d+) test=([01])')
|
||||
|
||||
|
||||
def screen_point(x, y, rotation=0):
|
||||
if rotation == 0: return x, y
|
||||
if rotation == 90: return y, 479-x
|
||||
if rotation == 180: return 479-x, 479-y
|
||||
if rotation == 270: return 479-y, x
|
||||
raise ValueError('Rotatie moet 0, 90, 180 of 270 zijn.')
|
||||
|
||||
|
||||
def verify(data):
|
||||
if data.get('screen') != [480,480] or len(data.get('points',[])) != 5:
|
||||
raise ValueError('Verwacht vijf meetpunten op een 480x480 GT911-scherm.')
|
||||
result=[]
|
||||
for p,(name,target) in zip(data['points'],TARGETS):
|
||||
if p.get('name') != name or len(p.get('samples',[])) != 3:
|
||||
raise ValueError('Volg de vijf doelen; ieder doel vereist drie aparte tikken.')
|
||||
samples=[]
|
||||
for xy in p['samples']:
|
||||
if len(xy)!=2 or any(type(v) is not int or not 0<=v<480 for v in xy):
|
||||
raise ValueError('Touchcoördinaten vallen buiten 480x480.')
|
||||
samples.append(screen_point(*xy, data.get('rotation',0)))
|
||||
center=tuple(statistics.median(x[i] for x in samples) for i in (0,1))
|
||||
error=math.dist(center,target)
|
||||
spread=max(math.dist(x,center) for x in samples)
|
||||
result.append(dict(name=name,error_px=round(error,1),spread_px=round(spread,1)))
|
||||
if error>16 or spread>18:
|
||||
raise ValueError(f'{name}: fout {error:.1f}px / spreiding {spread:.1f}px. Controleer rotatie en GT911-transform; geen ADC-fit gebruiken.')
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--port', help='USB-poort; firmware moet het GT911-meetscherm tonen')
|
||||
parser.add_argument('--output',type=Path,default=Path('measurements-guition.json'))
|
||||
parser.add_argument('--input',type=Path,help='Controleer een bestaand meetbestand zonder USB')
|
||||
parser.add_argument('--rotation',type=int,choices=(0,90,180,270),default=0)
|
||||
args=parser.parse_args()
|
||||
try:
|
||||
if args.input:
|
||||
data=json.loads(args.input.read_text())
|
||||
else:
|
||||
import serial
|
||||
if not args.port: raise ValueError('--port is nodig voor een fysieke meting.')
|
||||
if args.output.exists(): raise ValueError('Meetbestand bestaat al; kies een andere naam.')
|
||||
data=dict(screen=[480,480],rotation=args.rotation,points=[])
|
||||
with serial.Serial(args.port,115200,timeout=.2) as port:
|
||||
for name,xy in TARGETS:
|
||||
input(f'{name} {xy}: druk Enter en tik daarna drie keer alleen dit kruisje, met loslaten ertussen. ')
|
||||
port.reset_input_buffer()
|
||||
samples=[]; deadline=time.monotonic()+90; last=0
|
||||
while len(samples)<3 and time.monotonic()<deadline:
|
||||
m=PATTERN.search(port.readline().decode(errors='replace'))
|
||||
if not m: continue
|
||||
if m[3]!='1': raise ValueError('Het geïsoleerde GT911-meetscherm is niet actief.')
|
||||
if time.monotonic()-last < .4:continue
|
||||
samples.append([int(m[1]),int(m[2])]);last=time.monotonic()
|
||||
print(f' {len(samples)}/3',flush=True)
|
||||
if len(samples)!=3:raise ValueError(f'{name}: onvoldoende tikken ontvangen.')
|
||||
data['points'].append(dict(name=name,samples=samples))
|
||||
write_file(args.output,json.dumps(data,indent=2)+'\n')
|
||||
for row in verify(data):print(row)
|
||||
print('PASS: alle vijf GT911-doelen geverifieerd; geen HA-acties uitgevoerd.')
|
||||
except (ValueError,OSError,TypeError,KeyError,EOFError) as exc:
|
||||
parser.exit(1,f'Fout: {exc}\n')
|
||||
except KeyboardInterrupt:
|
||||
parser.exit(130,'Meting afgebroken.\n')
|
||||
|
||||
|
||||
if __name__=='__main__': main()
|
||||
Reference in New Issue
Block a user