Hardcode HA token in CONFIG block — set once, works on all devices

Replaced parent-frame auto-detect (blocked by HA iframe sandboxing)
with a CONFIG_HA_TOKEN constant at the top of the script.
Set it once in the file, works on every device automatically.
Falls back to localStorage/manual entry if CONFIG is empty.
This commit is contained in:
2026-05-18 11:54:56 +10:00
parent 9224a3fd58
commit b754c0cab6
+16 -27
View File
@@ -126,7 +126,7 @@ input[type=time]::-webkit-calendar-picker-indicator{filter:invert(.5)}
</div> </div>
<div id="settings-modal" class="ov" onclick="ovc(event,'settings-modal')"> <div id="settings-modal" class="ov" onclick="ovc(event,'settings-modal')">
<div class="modal"><h2>⚙ Settings</h2> <div class="modal"><h2>⚙ Settings</h2>
<div id="auto-auth-info" class="ibox" style="display:none"><strong>Auto-authenticated</strong> via Home Assistant session. No token needed — works on any device logged into HA.</div> <div id="auto-auth-info" class="ibox" style="display:none"><strong>Token configured in file</strong> — credentials are set in parental_controls.html. To change them, edit the CONFIG section at the top of the script.</div>
<div class="fg"><label>Home Assistant URL</label><input id="set-url" class="fi" type="text"/></div> <div class="fg"><label>Home Assistant URL</label><input id="set-url" class="fi" type="text"/></div>
<div class="fg"><label>Long-Lived Access Token</label><input id="set-tok" class="fi" type="password"/></div> <div class="fg"><label>Long-Lived Access Token</label><input id="set-tok" class="fi" type="password"/></div>
<div class="ibox"><strong>Config not saving?</strong> Go to HA → Developer Tools → Actions → call <code>input_text.reload</code>, then hard-refresh this page.</div> <div class="ibox"><strong>Config not saving?</strong> Go to HA → Developer Tools → Actions → call <code>input_text.reload</code>, then hard-refresh this page.</div>
@@ -146,21 +146,20 @@ let ws,wsId=1,pending={},retryTimer;
let states={},cfg={users:[]},prefs={url:'',token:''}; let states={},cfg={users:[]},prefs={url:'',token:''};
let devTargetUser=null,schedOpen={},renderTimer=null,lastSaveTime=0; let devTargetUser=null,schedOpen={},renderTimer=null,lastSaveTime=0;
let autoAuth=false; // true when auth was auto-detected from HA parent frame // =====================================================================
function getHAAuthFromParent(){ // CONFIGURATION — Set these once, works on every device automatically
// When loaded as panel_iframe inside HA, grab the session token from the parent frame // Generate a Long-Lived Access Token in HA: Profile → Security → Long-Lived Access Tokens
try{ // =====================================================================
const ha=window.parent.document.querySelector('home-assistant'); const CONFIG_HA_URL = ''; // e.g. 'https://ha.hideawaygaming.com.au' — leave blank to auto-detect from page URL
if(ha&&ha.hass&&ha.hass.auth&&ha.hass.auth.data&&ha.hass.auth.data.access_token){ const CONFIG_HA_TOKEN = ''; // Paste your Long-Lived Access Token here
return{url:window.location.origin,token:ha.hass.auth.data.access_token}; // =====================================================================
}
}catch(e){/* cross-origin or not in HA iframe */}
return null;
}
function loadPrefs(){ function loadPrefs(){
// 1. Try auto-detect from HA parent frame (works on any device if logged into HA) // 1. Use hardcoded config if token is set (works on all devices, no setup needed)
const ha=getHAAuthFromParent(); if(CONFIG_HA_TOKEN){
if(ha){prefs=ha;autoAuth=true;return true;} prefs={url:CONFIG_HA_URL||window.location.origin,token:CONFIG_HA_TOKEN};
return true;
}
// 2. Fallback to localStorage (legacy / standalone use) // 2. Fallback to localStorage (legacy / standalone use)
try{const p=localStorage.getItem('pc2_prefs');if(p){prefs=JSON.parse(p);return true;}}catch(e){} try{const p=localStorage.getItem('pc2_prefs');if(p){prefs=JSON.parse(p);return true;}}catch(e){}
return false; return false;
@@ -184,9 +183,6 @@ function saveSettings(){
} }
function connect(){ function connect(){
setHA('conn');clearTimeout(retryTimer); setHA('conn');clearTimeout(retryTimer);
// Refresh token from parent frame on each connect (session tokens rotate)
const ha=getHAAuthFromParent();
if(ha){prefs.url=ha.url;prefs.token=ha.token;autoAuth=true;}
const wsUrl=prefs.url.replace(/^https?/,m=>m==='https'?'wss':'ws')+'/api/websocket'; const wsUrl=prefs.url.replace(/^https?/,m=>m==='https'?'wss':'ws')+'/api/websocket';
try{ try{
ws=new WebSocket(wsUrl); ws=new WebSocket(wsUrl);
@@ -199,13 +195,7 @@ function dispatch(msg){
switch(msg.type){ switch(msg.type){
case 'auth_required': ws.send(JSON.stringify({type:'auth',access_token:prefs.token})); break; case 'auth_required': ws.send(JSON.stringify({type:'auth',access_token:prefs.token})); break;
case 'auth_ok': onAuth(); break; case 'auth_ok': onAuth(); break;
case 'auth_invalid': { case 'auth_invalid': setHA('err'); toast('HA auth failed — check token in parental_controls.html','e'); break;
// If auto-auth failed, try refreshing token from parent once
const fresh=getHAAuthFromParent();
if(fresh&&fresh.token!==prefs.token){prefs.token=fresh.token;ws.close();connect();}
else{setHA('err');toast('HA auth failed — check token','e');}
break;
}
case 'result': case 'result':
if(pending[msg.id]){const{res,rej}=pending[msg.id];delete pending[msg.id];msg.success?res(msg):rej(new Error(msg.error?.message||'HA error'));} if(pending[msg.id]){const{res,rej}=pending[msg.id];delete pending[msg.id];msg.success?res(msg):rej(new Error(msg.error?.message||'HA error'));}
break; break;
@@ -235,7 +225,6 @@ async function onAuth(){
} }
function onStateChange({entity_id,new_state}){ function onStateChange({entity_id,new_state}){
states[entity_id]=new_state; states[entity_id]=new_state;
// Only re-parse config if the change didn't come from our own save (5s grace period)
if(entity_id===chunkId(0)){if(Date.now()-lastSaveTime>5000)parseCfg();schedRender();return;} if(entity_id===chunkId(0)){if(Date.now()-lastSaveTime>5000)parseCfg();schedRender();return;}
if(entity_id===DHCP){updateDHCP();schedRender();return;} if(entity_id===DHCP){updateDHCP();schedRender();return;}
if(entity_id.startsWith('device_tracker.')){schedRender();} if(entity_id.startsWith('device_tracker.')){schedRender();}
@@ -424,7 +413,7 @@ function renderDev(userId,dev){
function toggleSched(userId){schedOpen[userId]=!schedOpen[userId];render();} function toggleSched(userId){schedOpen[userId]=!schedOpen[userId];render();}
function buildColorPicker(presel){document.getElementById('col-picker').innerHTML=COLORS.map(c=>`<span class="copt${c===presel?' sel':''}" data-c="${c}" style="background:${c}" onclick="pickColor(this)"></span>`).join('');} function buildColorPicker(presel){document.getElementById('col-picker').innerHTML=COLORS.map(c=>`<span class="copt${c===presel?' sel':''}" data-c="${c}" style="background:${c}" onclick="pickColor(this)"></span>`).join('');}
function pickColor(el){document.querySelectorAll('.copt').forEach(o=>o.classList.remove('sel'));el.classList.add('sel');} function pickColor(el){document.querySelectorAll('.copt').forEach(o=>o.classList.remove('sel'));el.classList.add('sel');}
function openModal(id){if(id==='add-user-modal')openAddUserModal();else{if(id==='settings-modal'){document.getElementById('set-url').value=prefs.url;document.getElementById('set-tok').value=prefs.token;document.getElementById('auto-auth-info').style.display=autoAuth?'block':'none';}document.getElementById(id).classList.add('open');}} function openModal(id){if(id==='add-user-modal')openAddUserModal();else{if(id==='settings-modal'){document.getElementById('set-url').value=prefs.url;document.getElementById('set-tok').value=prefs.token;document.getElementById('auto-auth-info').style.display=CONFIG_HA_TOKEN?'block':'none';}document.getElementById(id).classList.add('open');}}
function closeModal(id){document.getElementById(id).classList.remove('open');} function closeModal(id){document.getElementById(id).classList.remove('open');}
function ovc(e,id){if(e.target===e.currentTarget)closeModal(id);} function ovc(e,id){if(e.target===e.currentTarget)closeModal(id);}
function setHA(state){ function setHA(state){