feat: KoePi/Bestellungen/Presence Attribute erweitert - Details statt nur Zahlen

This commit is contained in:
2026-07-10 20:11:18 +02:00
parent bbf937c671
commit ccef4a3f4d

View File

@@ -234,6 +234,7 @@ function publishDiscovery() {
name: '3D Bestellungen offen', name: '3D Bestellungen offen',
unique_id: 'dickendock_bestellungen_offen', unique_id: 'dickendock_bestellungen_offen',
state_topic: `${BASE_TOPIC}/druck/bestellungen_offen`, state_topic: `${BASE_TOPIC}/druck/bestellungen_offen`,
json_attributes_topic: `${BASE_TOPIC}/druck/bestellungen_offen_attributes`,
icon: 'mdi:cube-send', icon: 'mdi:cube-send',
unit_of_measurement: 'Bestellungen', state_class: 'measurement', unit_of_measurement: 'Bestellungen', state_class: 'measurement',
availability_topic: AVAILABILITY_TOPIC, device: DEVICE, availability_topic: AVAILABILITY_TOPIC, device: DEVICE,
@@ -362,14 +363,19 @@ function publishKoepiState({ offers, changed }) {
if (!client?.connected) return; if (!client?.connected) return;
const list = [...(offers || [])].sort((a, b) => parsePrice(a.price) - parsePrice(b.price)); const list = [...(offers || [])].sort((a, b) => parsePrice(a.price) - parsePrice(b.price));
let state = list.length // State: nur die Anzahl (bleibt immer kurz und stabil) — die eigentlichen
? list.map(o => `${o.retailer}: ${o.price}`).join(' | ') // Angebote stehen vollständig in den Attributen
const state = list.length ? `${list.length} Angebote` : 'keine Angebote';
// Lesbarer Mehrzeiler fürs Dashboard, zusätzlich zur strukturierten Liste
const listeText = list.length
? list.map(o => `${o.retailer}: ${o.price}${o.validity || o.dateRange ? ' (' + (o.validity || o.dateRange) + ')' : ''}`).join('\n')
: 'keine Angebote'; : 'keine Angebote';
if (state.length > 255) state = state.slice(0, 252) + '…';
client.publish(`${BASE_TOPIC}/koepi/state`, state, { retain: true }); client.publish(`${BASE_TOPIC}/koepi/state`, state, { retain: true });
client.publish(`${BASE_TOPIC}/koepi/attributes`, JSON.stringify({ client.publish(`${BASE_TOPIC}/koepi/attributes`, JSON.stringify({
angebote: list, angebote: list,
liste_text: listeText,
anzahl: list.length, anzahl: list.length,
letzter_check: new Date().toISOString(), letzter_check: new Date().toISOString(),
}), { retain: true }); }), { retain: true });
@@ -383,12 +389,22 @@ function publishKoepiState({ offers, changed }) {
// Wird jede Minute vom Scheduler in index.js aufgerufen // Wird jede Minute vom Scheduler in index.js aufgerufen
function publishPresence() { function publishPresence() {
if (!client?.connected) return; if (!client?.connected) return;
const online = db.prepare(` const alleNutzer = db.prepare(`
SELECT username FROM users WHERE last_active_at > datetime('now','localtime','-5 minutes') SELECT username, last_active_at,
CASE WHEN last_active_at > datetime('now','localtime','-5 minutes') THEN 1 ELSE 0 END AS online
FROM users
ORDER BY last_active_at DESC
`).all(); `).all();
const online = alleNutzer.filter(u => !!u.online);
client.publish(`${BASE_TOPIC}/presence/state`, String(online.length), { retain: true }); client.publish(`${BASE_TOPIC}/presence/state`, String(online.length), { retain: true });
client.publish(`${BASE_TOPIC}/presence/attributes`, JSON.stringify({ client.publish(`${BASE_TOPIC}/presence/attributes`, JSON.stringify({
nutzer: online.map(u => u.username), online: online.map(u => ({ benutzer: u.username, zuletzt_aktiv: u.last_active_at })),
alle_nutzer: alleNutzer.map(u => ({
benutzer: u.username,
zuletzt_aktiv: u.last_active_at,
online: !!u.online,
})),
}), { retain: true }); }), { retain: true });
} }
@@ -495,6 +511,14 @@ function publishDruckStatistik() {
} }
const offen = allOrders.length - byStatus.abgeschlossen; const offen = allOrders.length - byStatus.abgeschlossen;
// Konkrete offene Bestellungen (nicht abgeschlossen) mit Besitzer, für die Attribute
const offeneBestellungen = db.prepare(`
SELECT o.id, o.name, o.status, o.bezahlt, o.abgeholt, o.created_at, u.username
FROM orders o JOIN users u ON u.id = o.user_id
WHERE NOT (o.bezahlt = 1 AND o.abgeholt = 1)
ORDER BY o.created_at DESC
`).all();
const pub = (topic, val) => client.publish(`${BASE_TOPIC}/${topic}`, String(val), { retain: true }); const pub = (topic, val) => client.publish(`${BASE_TOPIC}/${topic}`, String(val), { retain: true });
pub('druck/umsatz_monat', round2(umsatzMonat)); pub('druck/umsatz_monat', round2(umsatzMonat));
pub('druck/umsatz_gesamt', round2(umsatzGesamt)); pub('druck/umsatz_gesamt', round2(umsatzGesamt));
@@ -507,6 +531,9 @@ function publishDruckStatistik() {
pub('druck/nettogewinn_monat', round2(nettoMonat)); pub('druck/nettogewinn_monat', round2(nettoMonat));
pub('druck/nettogewinn_gesamt', round2(nettoGesamt)); pub('druck/nettogewinn_gesamt', round2(nettoGesamt));
pub('druck/bestellungen_offen', offen); pub('druck/bestellungen_offen', offen);
client.publish(`${BASE_TOPIC}/druck/bestellungen_offen_attributes`, JSON.stringify({
bestellungen: offeneBestellungen,
}), { retain: true });
pub('druck/bestellungen_warteliste', byStatus.warteliste); pub('druck/bestellungen_warteliste', byStatus.warteliste);
pub('druck/bestellungen_in_arbeit', byStatus.in_arbeit); pub('druck/bestellungen_in_arbeit', byStatus.in_arbeit);
pub('druck/bestellungen_fertig', byStatus.fertig); pub('druck/bestellungen_fertig', byStatus.fertig);