Add configurable water-softener diagram to Dashboard
Per-asset diagram_config (JSONB) maps live MQTT field_keys to brine/resin tank roles, editable on the Assets page with dropdowns populated from that asset's actual telemetry. Dashboard renders brine + 1-2 resin tanks with color-coded fill levels, an "in service" tank indicator, and an animated flow/regen indicator when regeneration is active.
This commit is contained in:
parent
eca07750d2
commit
4f048a9629
9 changed files with 4779 additions and 11 deletions
|
|
@ -24,6 +24,14 @@ export async function initDb() {
|
|||
CREATE INDEX IF NOT EXISTS plant_assets_type_idx ON plant_assets (asset_type);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS plant_assets_topic_prefix_idx ON plant_assets (mqtt_topic_prefix) WHERE mqtt_topic_prefix IS NOT NULL;
|
||||
|
||||
-- Per-asset visual diagram config (currently only rendered for
|
||||
-- water_softener) — which field_key maps to which role in the diagram
|
||||
-- (tank A/B capacity, brine level, regen active, tank in service), tank
|
||||
-- count, and each tank's max capacity in litres (not itself published by
|
||||
-- the device, only configured on it, so it has to be entered here).
|
||||
-- NULL until someone sets it up on the Assets page.
|
||||
ALTER TABLE plant_assets ADD COLUMN IF NOT EXISTS diagram_config JSONB;
|
||||
|
||||
-- Raw telemetry history, one row per MQTT message. field_key is whatever
|
||||
-- topic segment(s) follow the asset's mqtt_topic_prefix (see lib/mqtt.js) —
|
||||
-- generic across any asset type, never a fixed column set. Both a numeric
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const ASSET_TYPES = ['boiler', 'water_softener', 'calorifier', 'pump']
|
|||
|
||||
const WRITABLE_FIELDS = [
|
||||
'name', 'asset_type', 'location', 'make_model', 'serial_no',
|
||||
'install_date', 'notes', 'active', 'mqtt_topic_prefix',
|
||||
'install_date', 'notes', 'active', 'mqtt_topic_prefix', 'diagram_config',
|
||||
]
|
||||
|
||||
export async function assetRoutes(app, opts) {
|
||||
|
|
@ -35,6 +35,16 @@ export async function assetRoutes(app, opts) {
|
|||
return rows[0]
|
||||
})
|
||||
|
||||
// GET /api/assets/:id/latest — this asset's known field_keys, for populating
|
||||
// the diagram-config field-mapping dropdowns (Assets page).
|
||||
app.get('/api/assets/:id/latest', { preHandler: requireCap('view') }, async (req) => {
|
||||
const { rows } = await pool.query(
|
||||
'SELECT asset_id, field_key, value_numeric, value_text, updated_at FROM plant_asset_latest WHERE asset_id = $1 ORDER BY field_key',
|
||||
[req.params.id]
|
||||
)
|
||||
return rows
|
||||
})
|
||||
|
||||
// POST /api/assets — create
|
||||
app.post('/api/assets', { preHandler: requireCap('manage_assets') }, async (req, reply) => {
|
||||
const b = req.body || {}
|
||||
|
|
@ -45,13 +55,13 @@ export async function assetRoutes(app, opts) {
|
|||
|
||||
const { rows } = await pool.query(
|
||||
`INSERT INTO plant_assets
|
||||
(name, asset_type, location, make_model, serial_no, install_date, notes, active, mqtt_topic_prefix)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
||||
(name, asset_type, location, make_model, serial_no, install_date, notes, active, mqtt_topic_prefix, diagram_config)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
|
||||
RETURNING *`,
|
||||
[
|
||||
b.name.trim(), b.asset_type, b.location || null, b.make_model || null, b.serial_no || null,
|
||||
b.install_date || null, b.notes || null, b.active !== undefined ? b.active : true,
|
||||
b.mqtt_topic_prefix || null,
|
||||
b.mqtt_topic_prefix || null, b.diagram_config || null,
|
||||
]
|
||||
)
|
||||
await refreshAssetMap().catch(err => app.log.warn(`mqtt asset map refresh failed: ${err.message}`))
|
||||
|
|
@ -77,12 +87,13 @@ export async function assetRoutes(app, opts) {
|
|||
const { rows } = await pool.query(
|
||||
`UPDATE plant_assets SET
|
||||
name = $1, asset_type = $2, location = $3, make_model = $4, serial_no = $5,
|
||||
install_date = $6, notes = $7, active = $8, mqtt_topic_prefix = $9
|
||||
WHERE id = $10
|
||||
install_date = $6, notes = $7, active = $8, mqtt_topic_prefix = $9, diagram_config = $10
|
||||
WHERE id = $11
|
||||
RETURNING *`,
|
||||
[
|
||||
next.name, next.asset_type, next.location, next.make_model, next.serial_no,
|
||||
next.install_date, next.notes, next.active, next.mqtt_topic_prefix, req.params.id,
|
||||
next.install_date, next.notes, next.active, next.mqtt_topic_prefix, next.diagram_config,
|
||||
req.params.id,
|
||||
]
|
||||
)
|
||||
await refreshAssetMap().catch(err => app.log.warn(`mqtt asset map refresh failed: ${err.message}`))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue