88 lines
2.7 KiB
Python
88 lines
2.7 KiB
Python
# sensgw/writer.py
|
|
from __future__ import annotations
|
|
|
|
import datetime as dt
|
|
|
|
from .db import Database
|
|
|
|
ALLOWED_METRICS = {
|
|
"temp_c",
|
|
"humidity_rh",
|
|
"pressure_pa",
|
|
"light_lux",
|
|
"soil_moist",
|
|
"co2_ppm",
|
|
"voltage_v",
|
|
"current_a",
|
|
"resistance_ohm",
|
|
"freq_hz",
|
|
"power_w",
|
|
}
|
|
|
|
|
|
class Writer:
|
|
def __init__(self, db: Database):
|
|
self.db = db
|
|
|
|
async def write_metric(
|
|
self,
|
|
*,
|
|
ts: dt.datetime,
|
|
device_id: int,
|
|
location_id: int | None,
|
|
metric: str,
|
|
value: float,
|
|
) -> None:
|
|
if metric not in ALLOWED_METRICS:
|
|
raise ValueError(f"Metric not allowed: {metric}")
|
|
|
|
assert self.db.pool is not None
|
|
# Safe because we validate metric against allow-list above.
|
|
col = metric
|
|
|
|
async with self.db.pool.acquire() as con:
|
|
async with con.transaction():
|
|
await con.execute(
|
|
f"""
|
|
insert into sensor_data (ts, device_id, location_id, {col})
|
|
values ($1, $2, $3, $4)
|
|
on conflict (device_id, ts) do update
|
|
set {col} = excluded.{col},
|
|
location_id = coalesce(excluded.location_id, sensor_data.location_id)
|
|
""",
|
|
ts,
|
|
device_id,
|
|
location_id,
|
|
value,
|
|
)
|
|
await con.execute(
|
|
"""
|
|
insert into device_status (device_id, last_seen, last_ok, updated_at)
|
|
values ($1, now(), now(), now())
|
|
on conflict (device_id) do update
|
|
set last_seen = excluded.last_seen,
|
|
last_ok = excluded.last_ok,
|
|
updated_at = excluded.updated_at,
|
|
last_error_at = null,
|
|
last_error = null
|
|
""",
|
|
device_id,
|
|
)
|
|
|
|
async def write_error(self, *, device_id: int, error: str) -> None:
|
|
assert self.db.pool is not None
|
|
async with self.db.pool.acquire() as con:
|
|
await con.execute(
|
|
"""
|
|
insert into device_status (device_id, last_seen, last_error_at, last_error, updated_at)
|
|
values ($1, now(), now(), $2, now())
|
|
on conflict (device_id) do update
|
|
set last_seen = excluded.last_seen,
|
|
last_error_at = excluded.last_error_at,
|
|
last_error = excluded.last_error,
|
|
updated_at = excluded.updated_at
|
|
""",
|
|
device_id,
|
|
error[:2000],
|
|
)
|