Recipe: Sensor chart¶
This app shows the device's own temperature over the last few hours.
It needs no network and no broker. The only setting is how often to take a sample. If your device has a temperature sensor, the app works as soon as you paste it in. If it has none, the app stays out of the rotation instead of showing a wrong zero.
What you get¶
A dim line of the temperature over the last four hours, with the current reading in bright
orange at the right edge, for example 21.4°.
The script¶
In the web UI, open the Scripts tab, create a script called Trend, paste this in and
save. The settings declared at the top of the file then appear under Apps: press ⚙
on that app's row. New to all this?
Tutorial 1: Draw something takes it slowly.
# @name Trend
# @desc Device temperature, recent history
# @author awtrix-ng
# @version 1.0
# @config every number "Sample every" default=15 min=1 max=60 unit=min
# @config trend color "Trend line" default=#224466
# @config now color "Reading" default=#FF8800
class Trend
var hist # up to 16 samples, oldest first
var label, x # the current reading as text, and where it starts
var shown # the value the label was built from
var period, ticks
var trend, now_c
def init()
self.hist = store.get("hist")
if self.hist == nil
self.hist = []
end
self.label = nil
self.x = 0
self.shown = nil
self.period = store.get("every") * 60
self.ticks = 0
self.trend = store.get("trend")
self.now_c = store.get("now")
end
def loop()
var t = sensor.temperature()
if t == nil
return
end
var r = round(t, 1)
if r != self.shown
self.shown = r
self.label = str(r) + "°"
self.x = width() - text_ink_width(self.label)
end
if self.ticks <= 0
self.ticks = self.period
self.hist.push(r)
if size(self.hist) > 16
self.hist.remove(0)
end
store.set("hist", self.hist)
end
self.ticks -= 1
end
def should_show()
return self.label != nil
end
def draw()
clear()
if self.label == nil
return
end
if size(self.hist) >= 2
line_chart(self.hist, self.trend)
end
text(self.x, 6, self.label, self.now_c)
end
end
return Trend()
With the default of fifteen minutes, the full chart covers four hours. Set it to 1 while you test, so you can watch it fill.
How it works¶
A missing sensor answers nil, not zero. Then should_show() returns false and the
rotation skips the app. The display never shows a wrong 0.0°. sensor.temperature(),
humidity(), pressure(), light(), battery() and battery_volts() all work this way,
so check every one of them for nil.
The if self.label == nil at the top of draw() is a second check. The app can still be
brought to the display directly over the API. Without the check, a missing sensor would
show ERR:.
Values are always in Celsius. settings.get("useCelsius") tells you what the user chose
for the device. If you want to follow it, convert the value yourself.
One loop(), two speeds. The label follows the sensor every second, so the number on
the display is current. The history takes a sample only every fifteen minutes. Sixteen
values at one per second would cover only sixteen seconds.
The label is rebuilt only when the value changes. str(r) + "°" creates a new string
every time it runs, and the temperature often stays the same for minutes. The same check
also stores the x position, so draw() never measures anything.
Always round(r, 1) before str(). A raw sensor value prints all its decimals, and
21.399999618530273 does not fit on a 32 pixel display.
The chart is drawn first and dim, the number second and bright. Both use the same
eight rows, and what is drawn last is on top. line_chart() fills the full display width and
takes at most sixteen values, so the history is trimmed to sixteen. It needs at least two
values to draw anything, so the app checks size first.
Automatic scaling is on by default: the line uses the full display height for the range the
data covers. That is right for temperature, where a change of two degrees is interesting.
Turn it off with line_chart(list, color, false). Then the scale is fixed at 0 to 8, one step
per pixel row, so the values must already be pixel heights. Whole numbers are drawn: 21.4
counts as 21.
Making it yours¶
Chart something else. Use sensor.humidity() instead and change the unit in the
label. The rest of the app stays the same.
Chart something from the network. Replace the sensor call with the fetch from
tutorial 3.
Show the direction instead of the history. Keep only the oldest and newest values and draw an arrow. On a small display, one symbol is often easier to read than sixteen values.
Use bars instead of a line. bar_chart() takes the same three arguments. Bars suit
separate values, like hourly rain. A line suits a value that changes slowly, like
temperature.
Share the value with other apps. shared.set("temp", r) makes it readable for every
app on the device as Trend.temp. Other apps then do not need their own copy of this code.
Related¶
- Brightness & sensors – what each board measures
- Charts & drawing – all chart calls
- Scripting guide –
sharedand the full sensor reference