fix: add topic selection checkbox that auto-creates datapoints

When a user checks a topic checkbox, a datapoint is automatically
created and the broker subscribes to that topic for live updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Mueller 2026-04-06 20:28:49 +02:00
parent 086b470860
commit 73179f3040
2 changed files with 41 additions and 4 deletions

View File

@ -8,6 +8,7 @@ interface Topic {
topic: string; topic: string;
last_payload: string | null; last_payload: string | null;
json_path: string | null; json_path: string | null;
selected: number;
ignored: number; ignored: number;
discovered_at: string; discovered_at: string;
} }
@ -46,6 +47,15 @@ export default function MqttTopics() {
} }
} }
async function handleSelect(id: number, selected: boolean) {
try {
await api.put(`/api/mqtt/brokers/${brokerId}/topics/${id}`, { selected: selected ? 1 : 0 });
load();
} catch (e) {
setError((e as Error).message);
}
}
async function handleIgnore(id: number, ignored: number) { async function handleIgnore(id: number, ignored: number) {
try { try {
await api.put(`/api/mqtt/brokers/${brokerId}/topics/${id}`, { ignored: ignored ? 0 : 1 }); await api.put(`/api/mqtt/brokers/${brokerId}/topics/${id}`, { ignored: ignored ? 0 : 1 });
@ -100,6 +110,7 @@ export default function MqttTopics() {
<table className="w-full text-sm"> <table className="w-full text-sm">
<thead className="bg-gray-50"> <thead className="bg-gray-50">
<tr> <tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase w-10">Aktiv</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Topic</th> <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Topic</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Letzter Wert</th> <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">Letzter Wert</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">JSON-Pfad</th> <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">JSON-Pfad</th>
@ -108,10 +119,18 @@ export default function MqttTopics() {
</thead> </thead>
<tbody className="divide-y divide-gray-100"> <tbody className="divide-y divide-gray-100">
{filtered.length === 0 && ( {filtered.length === 0 && (
<tr><td colSpan={4} className="px-4 py-6 text-center text-gray-400">Keine Topics gefunden</td></tr> <tr><td colSpan={5} className="px-4 py-6 text-center text-gray-400">Keine Topics gefunden</td></tr>
)} )}
{filtered.map((t) => ( {filtered.map((t) => (
<tr key={t.id} className="hover:bg-gray-50"> <tr key={t.id} className={`hover:bg-gray-50 ${t.selected ? 'bg-green-50' : ''}`}>
<td className="px-4 py-2 text-center">
<input
type="checkbox"
checked={!!t.selected}
onChange={(e) => handleSelect(t.id, e.target.checked)}
className="w-4 h-4 accent-green-500"
/>
</td>
<td className="px-4 py-2 font-mono text-xs text-gray-700">{t.topic}</td> <td className="px-4 py-2 font-mono text-xs text-gray-700">{t.topic}</td>
<td className="px-4 py-2 text-xs text-gray-500 max-w-xs truncate">{t.last_payload ?? '-'}</td> <td className="px-4 py-2 text-xs text-gray-500 max-w-xs truncate">{t.last_payload ?? '-'}</td>
<td className="px-4 py-2"> <td className="px-4 py-2">

View File

@ -5,6 +5,9 @@ import {
startDiscovery, startDiscovery,
addManualTopic, addManualTopic,
} from '../../modules/mqtt/topic-discovery'; } from '../../modules/mqtt/topic-discovery';
import { createDatapointFromMqtt, getDatapointByTopicId } from '../../modules/datapoints/datapoint-manager';
import { getConnection } from './../../modules/mqtt/broker-manager';
import { getDb } from '../../db/index';
const router = Router({ mergeParams: true }); const router = Router({ mergeParams: true });
@ -21,8 +24,23 @@ router.get('/', (req, res): void => {
// PUT /api/mqtt/brokers/:brokerId/topics/:id // PUT /api/mqtt/brokers/:brokerId/topics/:id
router.put('/:id', (req, res): void => { router.put('/:id', (req, res): void => {
try { try {
const topic = updateTopic(parseInt(req.params.id), req.body); const topicId = parseInt(req.params.id);
if (!topic) { res.status(404).json({ error: 'Topic not found' }); return; } const topicRow = getDb().prepare('SELECT * FROM mqtt_topics WHERE id = ?').get(topicId) as any;
if (!topicRow) { res.status(404).json({ error: 'Topic not found' }); return; }
const topic = updateTopic(topicId, req.body);
// If selecting a topic, auto-create a datapoint
if (req.body.selected === 1 || req.body.selected === true) {
const existing = getDatapointByTopicId(topicId);
if (!existing) {
createDatapointFromMqtt(topicRow.broker_id, topicId, topicRow.topic);
}
// Subscribe to the topic on the broker connection
const client = getConnection(topicRow.broker_id);
if (client?.connected) client.subscribe(topicRow.topic);
}
res.json(topic); res.json(topic);
} catch (err) { } catch (err) {
res.status(500).json({ error: (err as Error).message }); res.status(500).json({ error: (err as Error).message });