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> {
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 timeout = setTimeout(() => controller.abort(), 5000);
const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
return res.ok;
if (!res.ok) return false;
const text = await res.text();
return text.includes('getPlainValue') || text.includes('getBulk');
} catch {
return false;
}
@ -152,9 +155,10 @@ export async function discoverStates(sourceId: number): Promise<number> {
try {
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 timeout = setTimeout(() => controller.abort(), 15000);
const timeout = setTimeout(() => controller.abort(), 30000);
const res = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
@ -163,8 +167,19 @@ export async function discoverStates(sourceId: number): Promise<number> {
throw new Error(`ioBroker API returned ${res.status}`);
}
const data = await res.json() as Record<string, unknown>;
const stateIds = Object.keys(data).slice(0, 5000);
const data = await res.json() as Record<string, { val?: unknown } | null>;
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(
`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;
// getBulk expects: /getBulk/stateID1,stateID2/
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 {
const controller = new AbortController();