feat: Home Assistant API tools (ha_get_state, ha_get_states, ha_call_service)
Register three HA orchestrator tools so Inara can read device states and control devices via the HA REST API. ha_call_service requires admin role and user confirmation. Also includes accumulated UI fixes (setProcessing helper, wasNewSession flag cleanup). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -384,6 +384,16 @@
|
||||
updateSendBtnTitle();
|
||||
}
|
||||
|
||||
function setProcessing(state) {
|
||||
if (state) {
|
||||
headerEmoji.classList.add('processing');
|
||||
document.body.classList.add('processing');
|
||||
} else {
|
||||
headerEmoji.classList.remove('processing');
|
||||
document.body.classList.remove('processing');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Settings dropdown ─────────────────────────────────────────
|
||||
settings_btn_el.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
@@ -1148,7 +1158,7 @@
|
||||
// ── Chat fetch + SSE handler ─────────────────────────────────
|
||||
// Extracted so the retry button can call it without re-adding the
|
||||
// user message to the DOM or currentHistory.
|
||||
async function _doSend(payload, thinkingDiv) {
|
||||
async function _doSend(payload, thinkingDiv, wasNewSession = false) {
|
||||
try {
|
||||
const res = await fetch('/chat', {
|
||||
method: 'POST',
|
||||
@@ -1238,13 +1248,13 @@
|
||||
activeController = new AbortController();
|
||||
sendBtn.style.display = 'none';
|
||||
stopBtn.style.display = 'flex';
|
||||
headerEmoji.classList.add('processing');
|
||||
setProcessing(true);
|
||||
startRunTimer();
|
||||
|
||||
await _doSend(payload, thinkingDiv);
|
||||
await _doSend(payload, thinkingDiv, false);
|
||||
|
||||
activeController = null;
|
||||
headerEmoji.classList.remove('processing');
|
||||
setProcessing(false);
|
||||
sendBtn.style.display = 'block';
|
||||
stopBtn.style.display = 'none';
|
||||
stopRunTimer();
|
||||
@@ -1265,7 +1275,7 @@
|
||||
syncHeight();
|
||||
sendBtn.style.display = 'none';
|
||||
stopBtn.style.display = 'flex';
|
||||
headerEmoji.classList.add('processing');
|
||||
setProcessing(true);
|
||||
startRunTimer();
|
||||
|
||||
activeController = new AbortController();
|
||||
@@ -1294,10 +1304,10 @@
|
||||
persona: CORTEX_PERSONA,
|
||||
};
|
||||
|
||||
await _doSend(payload, thinkingDiv);
|
||||
await _doSend(payload, thinkingDiv, wasNewSession);
|
||||
|
||||
activeController = null;
|
||||
headerEmoji.classList.remove('processing');
|
||||
setProcessing(false);
|
||||
sendBtn.style.display = 'block';
|
||||
stopBtn.style.display = 'none';
|
||||
stopRunTimer();
|
||||
@@ -1437,13 +1447,13 @@
|
||||
activeController = new AbortController();
|
||||
sendBtn.style.display = 'none';
|
||||
stopBtn.style.display = 'flex';
|
||||
headerEmoji.classList.add('processing');
|
||||
setProcessing(true);
|
||||
startRunTimer();
|
||||
|
||||
await _doOrchestrate(text, thinkingDiv, userMsgDiv);
|
||||
|
||||
activeController = null;
|
||||
headerEmoji.classList.remove('processing');
|
||||
setProcessing(false);
|
||||
sendBtn.style.display = 'block';
|
||||
stopBtn.style.display = 'none';
|
||||
stopRunTimer();
|
||||
@@ -1462,7 +1472,7 @@
|
||||
syncHeight();
|
||||
sendBtn.style.display = 'none';
|
||||
stopBtn.style.display = 'flex';
|
||||
headerEmoji.classList.add('processing');
|
||||
setProcessing(true);
|
||||
startRunTimer();
|
||||
|
||||
activeController = new AbortController();
|
||||
@@ -1476,7 +1486,7 @@
|
||||
await _doOrchestrate(text, thinkingDiv, userMsgDiv);
|
||||
|
||||
activeController = null;
|
||||
headerEmoji.classList.remove('processing');
|
||||
setProcessing(false);
|
||||
sendBtn.style.display = 'block';
|
||||
stopBtn.style.display = 'none';
|
||||
stopRunTimer();
|
||||
@@ -2215,4 +2225,4 @@
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,15 @@
|
||||
|
||||
.header-emoji.processing { animation: shimmer 0.75s ease-in-out infinite; }
|
||||
|
||||
@keyframes border-pulse {
|
||||
0%, 100% { box-shadow: inset 0 0 15px var(--amber-glow); }
|
||||
50% { box-shadow: inset 0 0 30px var(--amber-glow); }
|
||||
}
|
||||
|
||||
body.processing {
|
||||
animation: border-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
header .name { font-size: 1.1rem; font-weight: 600; color: var(--accent); }
|
||||
header .subtitle { font-size: 0.78rem; color: var(--muted); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user