Add resilient handover, live LED layout refresh and TV calibration profiles

This commit is contained in:
matze19999 committed 2026-10-06 19:05:06 +02:00
1 parent 4ab4fa034b
commit e55d37772a
26 files changed
+543 -134

No files matched your search

+15 -2
View File
@@ -1,6 +1,9 @@
'use strict';
var pending=[];
var targetDirty=false,receiver='Hyperion';
var profileDirty=false,profile='auto';
var profileNames={auto:'Auto',g4:'LG G4 (measured)',average:'Average (uniform)',custom:'Custom model'};
function showProfile(){document.getElementById('profile').textContent='Profile: '+profileNames[profile];document.getElementById('customEditor').style.display=profile==='custom'?'block':'none';document.getElementById('customProfile').disabled=profile!=='custom';}
function call(method,done,data){
if(typeof PalmServiceBridge==='undefined'){done({returnValue:false,errorText:'This app requires an LG TV with the native service installed.'});return;}
var bridge=new PalmServiceBridge(),timer,finished=false;
@@ -10,6 +13,12 @@ function call(method,done,data){
bridge.call('luna://org.webosbrew.histogramhyperion.service/'+method,JSON.stringify(data||{}));
}
function display(result){
if(result.calibrationProfile && !profileDirty){
profile=result.calibrationProfile;showProfile();
document.getElementById('customProfile').value=JSON.stringify({model:result.customModel||result.tvModel||'TV-MODEL',points:result.customPoints&&result.customPoints.length?result.customPoints:[]},null,2);
}
if(result.effectiveCalibration)document.getElementById('profileStatus').textContent='TV: '+(result.tvModel||'Unknown')+' · Active profile: '+profileNames[result.effectiveCalibration]+(result.effectiveCalibration==='average'?' · Spatial calibration needed for this model':'');
if(result.host && !targetDirty){
document.getElementById('host').value=result.host;document.getElementById('port').value=result.port;document.getElementById('piccapPort').value=result.piccapPort||19400;receiver=result.receiver;
document.getElementById('receiver').textContent='Receiver: '+receiver;
@@ -19,7 +28,7 @@ function display(result){
var t=result.connectionTest;
if(t)document.getElementById('testResult').textContent='Connection test: '+t.state+(t.state==='Passed'?' · '+t.ledCount+' LEDs · '+t.elapsedMs+' ms':'')+(t.error?' · '+t.error:'');
document.getElementById('testConnection').disabled=!!t && ['Connecting','Sending request','Checking authentication','Reading LED layout'].indexOf(t.state)>=0;
if(result.version)document.getElementById('diagnostics').textContent='Version '+result.version+' · '+(result.uid===0?'Root service':'Service needs root elevation')+' · '+result.captureState+' · Playback: '+result.playbackProtection+' · Capture recoveries: '+result.captureRecoveries+' (last: '+result.lastRecoveryMs+' ms)'+(result.lastOutputAgeMs>=0?' · Last histogram output: '+result.lastOutputAgeMs+' ms ago':'')+(result.retryScheduled?' · Retry in '+result.retryInMs+' ms · Attempts: '+result.retryAttempts:'')+(result.piccapError?' · '+result.piccapError:'');
if(result.version)document.getElementById('diagnostics').textContent='Version '+result.version+' · '+(result.uid===0?'Root service':'Service needs root elevation')+' · '+result.captureState+' · Playback: '+result.playbackProtection+' · Capture recoveries: '+result.captureRecoveries+' (last: '+result.lastRecoveryMs+' ms)'+(result.lastOutputAgeMs>=0?' · Last histogram output: '+result.lastOutputAgeMs+' ms ago':'')+(result.retryScheduled?' · Retry in '+result.retryInMs+' ms · Attempts: '+result.retryAttempts:'')+(result.piccapError?' · '+result.piccapError:'')+(result.piccapCooldownMs?' · PicCap retry in '+Math.ceil(result.piccapCooldownMs/1000)+' s':'')+' · Layout updates: '+(result.layoutUpdates||0)+(result.layoutError?' · Layout: '+result.layoutError:'');
var handover=document.getElementById('handover');handover.disabled=!result.piccapInstalled;
handover.setAttribute('aria-checked',String(!!result.piccapHandover));
handover.textContent=result.piccapInstalled?'Auto PicCap: '+(result.piccapHandover?'On':'Off'):'Auto PicCap: Not installed';
@@ -32,6 +41,7 @@ function display(result){
}
if(result.piccapHandover)document.getElementById('status').textContent+=' · HyperDRM · '+result.playbackProtection;
if(result.piccapHandover && !result.piccapTargetCompatible)document.getElementById('status').textContent+=' · Auto PicCap unavailable for remote IPv6 target';
if(result.colorValid && result.effectiveCalibration==='average')document.getElementById('status').textContent+=' · Average color output';
if(result.colorValid)document.getElementById('status').textContent+=result.spatialOutput?' · '+result.ledCount+' LEDs from 16 color regions'+(result.receiver==='HyperHDR'?' · Spatial image output':''):' · Output RGB: '+result.outputColor.join(', ');
else if(result.returnValue!==false)document.getElementById('status').textContent+=' · No current color output';
if(result.blackBarDetection && result.running)document.getElementById('status').textContent+=result.letterboxDetected?' · Top/bottom black bars detected':' · Full-screen capture';
@@ -62,4 +72,7 @@ document.getElementById('saveTarget').onclick=function(){
button.disabled=true;call('settings',function(r){button.disabled=false;if(r.returnValue===false){document.getElementById('error').textContent=r.errorText;return;}targetDirty=false;document.getElementById('token').value='';document.getElementById('clearToken').checked=false;call('status',display);},settings);
};
document.getElementById('testConnection').onclick=function(){if(targetDirty){document.getElementById('error').textContent='Save receiver settings before testing the connection.';return;}this.disabled=true;call('testConnection',function(r){if(r.returnValue===false)document.getElementById('error').textContent=r.errorText;call('status',display);});};
setupRemoteNavigation(['start','stop','refresh','autostart','handover','receiver','host','port','piccapPort','token','clearToken','saveTarget','testConnection'].map(function(id){return document.getElementById(id);}),document,window);
document.getElementById('profile').onclick=function(){var keys=['auto','g4','average','custom'];profile=keys[(keys.indexOf(profile)+1)%keys.length];profileDirty=true;showProfile();};
document.getElementById('customProfile').addEventListener('input',function(){profileDirty=true;});
document.getElementById('saveProfile').onclick=function(){var data={profile:profile},button=this;if(profile==='custom'){try{var parsed=JSON.parse(document.getElementById('customProfile').value);data.model=parsed.model;data.points=parsed.points;}catch(e){document.getElementById('error').textContent='Custom profile must be valid JSON.';return;}}button.disabled=true;call('calibration',function(r){button.disabled=false;if(r.returnValue===false){document.getElementById('error').textContent=r.errorText;return;}profileDirty=false;call('status',display);},data);};
setupRemoteNavigation(['start','stop','refresh','autostart','handover','receiver','host','port','piccapPort','token','clearToken','saveTarget','testConnection','profile','customProfile','saveProfile'].map(function(id){return document.getElementById(id);}),document,window);