feat: HA-Entitaeten fuer Media-Anfragen, offene Bestellungen und 3D-Druck-Monatsumsatz
This commit is contained in:
@@ -114,6 +114,40 @@ function publishDiscovery() {
|
||||
availability_topic: AVAILABILITY_TOPIC,
|
||||
device: DEVICE,
|
||||
}],
|
||||
['sensor', 'dickendock_media_anfragen', {
|
||||
name: 'Media Anfragen offen',
|
||||
unique_id: 'dickendock_media_anfragen',
|
||||
state_topic: `${BASE_TOPIC}/media/anfragen`,
|
||||
json_attributes_topic: `${BASE_TOPIC}/media/anfragen_attributes`,
|
||||
icon: 'mdi:movie-open-star',
|
||||
unit_of_measurement: 'Anfragen',
|
||||
state_class: 'measurement',
|
||||
availability_topic: AVAILABILITY_TOPIC,
|
||||
device: DEVICE,
|
||||
}],
|
||||
['sensor', 'dickendock_bestellungen_offen', {
|
||||
name: 'Bestellungen offen',
|
||||
unique_id: 'dickendock_bestellungen_offen',
|
||||
state_topic: `${BASE_TOPIC}/bestellungen/offen`,
|
||||
json_attributes_topic: `${BASE_TOPIC}/bestellungen/attributes`,
|
||||
icon: 'mdi:cube-send',
|
||||
unit_of_measurement: 'Bestellungen',
|
||||
state_class: 'measurement',
|
||||
availability_topic: AVAILABILITY_TOPIC,
|
||||
device: DEVICE,
|
||||
}],
|
||||
['sensor', 'dickendock_3ddruck_umsatz_monat', {
|
||||
name: '3D-Druck Umsatz (Monat)',
|
||||
unique_id: 'dickendock_3ddruck_umsatz_monat',
|
||||
state_topic: `${BASE_TOPIC}/druck/umsatz_monat`,
|
||||
json_attributes_topic: `${BASE_TOPIC}/druck/attributes`,
|
||||
icon: 'mdi:printer-3d',
|
||||
device_class: 'monetary',
|
||||
unit_of_measurement: '€',
|
||||
state_class: 'measurement',
|
||||
availability_topic: AVAILABILITY_TOPIC,
|
||||
device: DEVICE,
|
||||
}],
|
||||
];
|
||||
for (const [component, objectId, payload] of entities) {
|
||||
client.publish(`homeassistant/${component}/${objectId}/config`, JSON.stringify(payload), { retain: true });
|
||||
@@ -207,7 +241,89 @@ function publishPresence() {
|
||||
}), { retain: true });
|
||||
}
|
||||
|
||||
// Wird jede Minute vom Scheduler aufgerufen — offene (unquittierte) Media-Favoriten,
|
||||
// systemweit über alle Nutzer (spiegelt die Admin-Ansicht in "Media/Favoriten")
|
||||
function publishMediaAnfragen() {
|
||||
if (!client?.connected) return;
|
||||
const offen = db.prepare('SELECT COUNT(*) AS n FROM movie_favorites WHERE acknowledged=0').get().n;
|
||||
const liste = db.prepare(`
|
||||
SELECT f.title, u.username, f.added_at
|
||||
FROM movie_favorites f JOIN users u ON u.id = f.user_id
|
||||
WHERE f.acknowledged = 0
|
||||
ORDER BY f.added_at DESC LIMIT 20
|
||||
`).all();
|
||||
client.publish(`${BASE_TOPIC}/media/anfragen`, String(offen), { retain: true });
|
||||
client.publish(`${BASE_TOPIC}/media/anfragen_attributes`, JSON.stringify({ anfragen: liste }), { retain: true });
|
||||
}
|
||||
|
||||
// Wird jede Minute vom Scheduler aufgerufen — offene 3D-Druck-Bestellungen,
|
||||
// systemweit über alle Nutzer, Status-Verteilung als Attribut
|
||||
function publishBestellungen() {
|
||||
if (!client?.connected) return;
|
||||
const orders = db.prepare('SELECT status, bezahlt, abgeholt FROM orders').all();
|
||||
const byStatus = { warteliste: 0, in_arbeit: 0, fertig: 0, bezahlt: 0, abgeschlossen: 0 };
|
||||
for (const o of orders) {
|
||||
if (o.bezahlt && o.abgeholt) byStatus.abgeschlossen++;
|
||||
else if (o.bezahlt) byStatus.bezahlt++;
|
||||
else if (o.status in byStatus) byStatus[o.status]++;
|
||||
}
|
||||
const offen = orders.length - byStatus.abgeschlossen;
|
||||
client.publish(`${BASE_TOPIC}/bestellungen/offen`, String(offen), { retain: true });
|
||||
client.publish(`${BASE_TOPIC}/bestellungen/attributes`, JSON.stringify({
|
||||
...byStatus,
|
||||
gesamt: orders.length,
|
||||
}), { retain: true });
|
||||
}
|
||||
|
||||
function round2(n) { return Math.round((n || 0) * 100) / 100; }
|
||||
|
||||
// Umsatz eines einzelnen Auftrags — Festpreis falls gesetzt, sonst Summe der
|
||||
// Positions-Festpreise (spiegelt die Logik aus statistik/routes.js)
|
||||
function getOrderRevenue(order) {
|
||||
if (order.custom_price != null && order.custom_price > 0) return parseFloat(order.custom_price);
|
||||
const r = db.prepare(`
|
||||
SELECT COALESCE(SUM(custom_price * stueckzahl), 0) AS s
|
||||
FROM order_items WHERE order_id=? AND custom_price IS NOT NULL
|
||||
`).get(order.id);
|
||||
return r?.s || 0;
|
||||
}
|
||||
|
||||
// Wird jede Minute vom Scheduler aufgerufen — 3D-Druck-Umsatz im laufenden Kalendermonat,
|
||||
// systemweit über alle Nutzer (bezahlte Aufträge nach bezahlt_am, sonst created_at)
|
||||
function publishDruckStatistik() {
|
||||
if (!client?.connected) return;
|
||||
const now = new Date();
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
const monatsAnfang = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-01`;
|
||||
const heute = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
||||
|
||||
const allOrders = db.prepare('SELECT * FROM orders').all();
|
||||
const bezahltDiesenMonat = allOrders.filter(o => {
|
||||
if (!o.bezahlt) return false;
|
||||
const d = (o.bezahlt_am || o.created_at || '').slice(0, 10);
|
||||
return d >= monatsAnfang && d <= heute;
|
||||
});
|
||||
const umsatzMonat = bezahltDiesenMonat.reduce((s, o) => s + getOrderRevenue(o), 0);
|
||||
|
||||
const ausgabenMonat = db.prepare(`
|
||||
SELECT COALESCE(SUM(amount), 0) AS s FROM expenses WHERE date>=? AND date<=?
|
||||
`).get(monatsAnfang, heute).s;
|
||||
|
||||
const offeneAuftraege = allOrders.filter(o => !(o.bezahlt && o.abgeholt)).length;
|
||||
|
||||
client.publish(`${BASE_TOPIC}/druck/umsatz_monat`, String(round2(umsatzMonat)), { retain: true });
|
||||
client.publish(`${BASE_TOPIC}/druck/attributes`, JSON.stringify({
|
||||
ausgaben_monat: round2(ausgabenMonat),
|
||||
netto_monat: round2(umsatzMonat - ausgabenMonat),
|
||||
bezahlte_auftraege_monat: bezahltDiesenMonat.length,
|
||||
offene_auftraege_gesamt: offeneAuftraege,
|
||||
}), { retain: true });
|
||||
}
|
||||
|
||||
// Wird von index.js gesetzt, damit der HA-Button ohne Require-Zirkel den Check auslösen kann
|
||||
function setKoepiCheckHandler(fn) { koepiCheckHandler = fn; }
|
||||
|
||||
module.exports = { connectMqtt, publishKoepiState, publishPresence, setKoepiCheckHandler };
|
||||
module.exports = {
|
||||
connectMqtt, publishKoepiState, publishPresence, setKoepiCheckHandler,
|
||||
publishMediaAnfragen, publishBestellungen, publishDruckStatistik,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user