fix: correct ioBroker API endpoints for discovery and polling

- Discovery uses /states?pattern=* (not /objects?type=state)
- Stores initial values during discovery
- Test uses root endpoint to verify API help page
- getBulk URL with trailing slash

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Mueller 2026-04-07 05:20:17 +02:00
parent e76082df92
commit 3a4e762290
2 changed files with 23 additions and 7 deletions

View File

@ -84,12 +84,15 @@ function setStatus(id: number, status: string): void {
export async function testSource(host: string, port: number): Promise<boolean> { export async function testSource(host: string, port: number): Promise<boolean> {
try { try {
const url = `http://${host}:${port}/get/system.adapter.admin.0.alive`; // Root endpoint returns API help JSON — if it responds, the API is reachable
const url = `http://${host}:${port}/`;
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); const timeout = setTimeout(() => controller.abort(), 5000);
const res = await fetch(url, { signal: controller.signal }); const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeout); clearTimeout(timeout);
return res.ok; if (!res.ok) return false;
const text = await res.text();
return text.includes('getPlainValue') || text.includes('getBulk');
} catch { } catch {
return false; return false;
} }
@ -152,9 +155,10 @@ export async function discoverStates(sourceId: number): Promise<number> {
try { try {
setStatus(sourceId, 'discovering'); setStatus(sourceId, 'discovering');
const url = `http://${source.host}:${source.port}/objects?type=state&pattern=*`; // Use /states endpoint to get all state IDs with current values
const url = `http://${source.host}:${source.port}/states?pattern=*`;
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15000); const timeout = setTimeout(() => controller.abort(), 30000);
const res = await fetch(url, { signal: controller.signal }); const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeout); clearTimeout(timeout);
@ -163,8 +167,19 @@ export async function discoverStates(sourceId: number): Promise<number> {
throw new Error(`ioBroker API returned ${res.status}`); throw new Error(`ioBroker API returned ${res.status}`);
} }
const data = await res.json() as Record<string, unknown>; const data = await res.json() as Record<string, { val?: unknown } | null>;
const stateIds = Object.keys(data).slice(0, 5000); const entries = Object.entries(data).slice(0, 5000);
const stateIds = entries.map(([key]) => key);
// Also store initial values
const updateStmt = db.prepare(
'UPDATE iobroker_states SET last_value = ? WHERE source_id = ? AND state_id = ?'
);
for (const [key, val] of entries) {
if (val && val.val !== null && val.val !== undefined) {
updateStmt.run(String(val.val), sourceId, key);
}
}
const insertStmt = db.prepare( const insertStmt = db.prepare(
`INSERT OR IGNORE INTO iobroker_states (source_id, state_id) VALUES (?, ?)` `INSERT OR IGNORE INTO iobroker_states (source_id, state_id) VALUES (?, ?)`

View File

@ -20,8 +20,9 @@ async function pollSource(sourceId: number): Promise<void> {
if (selectedStates.length === 0) return; if (selectedStates.length === 0) return;
// getBulk expects: /getBulk/stateID1,stateID2/
const stateIds = selectedStates.map((s) => s.state_id).join(','); const stateIds = selectedStates.map((s) => s.state_id).join(',');
const url = `http://${source.host}:${source.port}/getBulk/${encodeURIComponent(stateIds)}`; const url = `http://${source.host}:${source.port}/getBulk/${stateIds}/`;
try { try {
const controller = new AbortController(); const controller = new AbortController();