aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/update_database.nim75
-rw-r--r--src/update_graphs.nim107
2 files changed, 182 insertions, 0 deletions
diff --git a/src/update_database.nim b/src/update_database.nim
new file mode 100644
index 0000000..1a27517
--- /dev/null
+++ b/src/update_database.nim
@@ -0,0 +1,75 @@
+import json
+import os
+import osproc
+import parsecfg
+import strformat
+import strutils
+
+type
+ CgiError = object of CatchableError
+ code: int
+
+proc newCgiError(code: int, message: string): ref CgiError =
+ result = newException(CgiError, message)
+ result.code = code
+
+proc readData(): string =
+ var size = parseInt(getEnv("CONTENT_LENGTH").string)
+ if size == 0:
+ return ""
+ result = newString(size)
+ if readBuffer(stdin, addr result[0], size) != 0:
+ raise newCgiError(400, "not enough data")
+
+proc main() =
+ if paramCount() != 2:
+ raise newCgiError(500, "one argument required")
+
+ let config = loadConfig(paramStr(1))
+ let databaseDir = config.getSectionValue("", "databaseDir")
+ if databaseDir == "":
+ raise newCgiError(500, "databaseDir is empty")
+
+ if getEnv("REQUEST_METHOD") != "POST":
+ raise newCgiError(400, "request method must be POST")
+
+ let j = parseJson(readData())
+
+ for alias, measurement in j:
+ let filename = normalizedPath(joinPath(databaseDir, alias & ".rrd"))
+
+ if not filename.startsWith(databaseDir):
+ raise newCgiError(400, "invalid database")
+ let
+ timestamp = measurement["timestamp"].getBiggestInt()
+ temperature = measurement["temperature"].getFloat()
+ humidity = measurement["humidity"].getFloat()
+ pressure = measurement["pressure"].getFloat()
+ battery = measurement["battery_potential"].getFloat()
+ data = &"{timestamp}:{temperature}:{humidity}:{pressure}:{battery}"
+ (_, ec) = execCmdEx(&"rrdtool update {filename} {data}")
+ if ec != 0:
+ raise newCgiError(500, "rrdtool failed")
+
+try:
+ main()
+ echo("Content-Type: text/plain")
+ echo("")
+except CgiError as e:
+ echo(&"Status: {e.code}")
+ echo("Content-Type: text/plain")
+ echo("")
+ echo(e.msg)
+ quit(0)
+except JsonParsingError as e:
+ echo(&"Status: 400 Bad Request")
+ echo("Content-Type: text/plain")
+ echo("")
+ echo(&"invalid json: {e.msg}")
+ quit(0)
+except Exception as e:
+ echo(&"Status: 500 Internal Server Error")
+ echo("Content-Type: text/plain")
+ echo("")
+ echo(e.msg)
+ quit(0)
diff --git a/src/update_graphs.nim b/src/update_graphs.nim
new file mode 100644
index 0000000..3f902a4
--- /dev/null
+++ b/src/update_graphs.nim
@@ -0,0 +1,107 @@
+import os
+import osproc
+import strformat
+
+let
+ databaseDir = getAppDir().parentDir() / "database"
+ outputDir = paramStr(2)
+ width = 800
+ height = 600
+
+proc graph(filename, title, vlabel: string, args: varargs[string, `$`]) =
+ var cmdline: seq[string] = @[
+ "graph", joinPath(outputDir, filename),
+ "--width", $width,
+ "--height", $height,
+ "--title", title,
+ "--vertical-label", vlabel,
+ ] & @args
+
+ echo(&"Generating {joinPath(outputDir, filename)} ...")
+
+ let p = startProcess("rrdtool", args=cmdline,
+ options={poStdErrToStdOut, poUsePath, poParentStreams})
+
+ if p.waitForExit() != 0:
+ quit(1)
+
+
+for database in walkFiles(joinPath(databaseDir, "*.rrd")):
+ let (_, base, _) = splitFile(database)
+
+ graph(&"{base}-temperature-today.png", "Temperature", "Celcius",
+ "--end", "23:59 today",
+ "--start", "00:00 today",
+ "--alt-autoscale",
+ "--left-axis-format", "%.1lf",
+ &"DEF:temp1={database}:temperature:AVERAGE",
+ &"DEF:temp2={database}:temperature:AVERAGE:end=00\\:00 today:start=end-24h",
+ "LINE1:temp1#000000:today",
+ "LINE1:temp2#FF0000:yesterday",
+ "SHIFT:temp2:86400",
+ )
+
+ graph(&"{base}-temperature-month.png", "Temperature", "Celcius",
+ "--end", "23:59 today",
+ "--start", "end-1month",
+ "--step", "86400",
+ "--alt-autoscale",
+ "--x-grid", "DAY:1:DAY:1:DAY:1:86400:%d",
+ &"DEF:avg={database}:temperature:AVERAGE",
+ &"DEF:min={database}:temperature:MIN",
+ &"DEF:max={database}:temperature:MAX",
+ "LINE1:avg#000000:average",
+ "LINE2:min#FF0000:min",
+ "LINE3:max#00FF00:max"
+ )
+
+ graph(&"{base}-humidity-today.png", "Relative humidity", "%",
+ "--end", "23:59 today",
+ "--start", "00:00 today",
+ &"DEF:humi1={database}:humidity:AVERAGE",
+ &"DEF:humi2={database}:humidity:AVERAGE:end=00\\:00 today:start=end-24h",
+ "LINE1:humi1#000000:today",
+ "LINE1:humi2#FF0000:yesterday",
+ "SHIFT:humi2:86400",
+ )
+
+ graph(&"{base}-humidity-month.png", "Relative humidity", "%",
+ "--end", "23:59 today",
+ "--start", "end-1month",
+ "--step", "86400",
+ "--alt-autoscale",
+ "--x-grid", "DAY:1:DAY:1:DAY:1:86400:%d",
+ &"DEF:avg={database}:humidity:AVERAGE",
+ &"DEF:min={database}:humidity:MIN",
+ &"DEF:max={database}:humidity:MAX",
+ "LINE1:avg#000000:average",
+ "LINE2:min#FF0000:min",
+ "LINE3:max#00FF00:max",
+ )
+
+ graph(&"{base}-pressure-today.png", "Pressure", "Pascal",
+ "--end", "23:59 today",
+ "--start", "00:00 today",
+ "--alt-autoscale",
+ "--left-axis-format", "%.1lf",
+ &"DEF:pres1={database}:pressure:AVERAGE",
+ &"DEF:pres2={database}:pressure:AVERAGE:end=00\\:00 today:start=end-24h",
+ "LINE1:pres1#000000:today",
+ "LINE1:pres2#FF0000:yesterday",
+ "SHIFT:pres2:86400",
+ )
+
+ graph(&"{base}-pressure-month.png", "Pressure", "Pascal",
+ "--end", "23:59 today",
+ "--start", "end-1month",
+ "--step", "86400",
+ "--alt-autoscale",
+ "--x-grid", "DAY:1:DAY:1:DAY:1:86400:%d",
+ "--left-axis-format", "%.1lf",
+ &"DEF:avg={database}:pressure:AVERAGE",
+ &"DEF:min={database}:pressure:MIN",
+ &"DEF:max={database}:pressure:MAX",
+ "LINE1:avg#000000:average",
+ "LINE2:min#FF0000:min",
+ "LINE3:max#00FF00:max",
+ )