Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python lightwall server

Python server for bluetooth-controlled light wall using NeoPixels/NeoMatrix

Configuration

Read from a gitignored .env via python-decouple:

Key Required Purpose
AUTH_USER yes HTTP basic auth username
AUTH_PASS yes HTTP basic auth password
PORT yes Port to serve on
FINNHUB_KEY no Fallback price source for stock mode
ALPHAVANTAGE_KEY no Fallback history source for stock mode

The two stock keys are only consulted if the primary source fails, so stock mode works without them.

Stock mode

/stock shows a 32 day sparkline for one ticker, with the symbol on the top row of panels and the current price, in whole dollars, on the bottom. The baseline is where the price sat 32 trading days ago; the filled area between it and the line is the move since, green above and red below.

Prices show no cents. There is nowhere to put a decimal point -- a 1px dot falls in the margin between panels, where a strut hides it -- and an implied decimal misreads: 7448 looks like $7,448 rather than $74.48.

It is a still image. The wall repaints only when the data actually changes, and the server does not even send a frame unless it would look different, so most refreshes touch nothing at all. Nothing fades, pulses or animates -- at this brightness any movement is distracting rather than informative. For the same reason there is no pause control: there is nothing to pause.

Gains are green and losses red, at exact hues 120 and 0 with saturation 100 and low lightness. At this pixel density up-is-green is worth keeping, since it reads instantly where any other pairing has to be learned.

Lightness is the only knob for subduing this. Two things that seem like they should work, and do not:

  • Nudging the hues off the primaries. "Emerald" 142 and "crimson" 355 put blue at 39% and 13% of the dominant channel, and the panels showed aquamarine and magenta. Blue bleed matters far more than the numbers suggest because the fill covers most of the display. Saturation 100 at exactly 120 and 0 is what holds the off-channels at zero.
  • Desaturating. A dark colour desaturated is just grey -- an amber at saturation 55, lightness 7 came out (27, 19, 8) and read as cream. Hue survives darkness; saturation does not.

What made the first version look like Christmas decoration was lightness alone: at lightness 38 the green was (0, 193, 0), near full output. It is 18 now.

Every colour is a named constant in the palette block above stockChart() in lightwall.ino -- tuning the relative weights means editing a number there and reflashing. The stockColors map in static/app.js mirrors it for the preview, deliberately lighter than the literal values because an LED at close range is far brighter than the same numbers on a monitor.

Brightness

Overall brightness is a slider on /stock, not a compile-time constant, because what looks right in person is far too bright for a webcam -- a camera has roughly half the dynamic range of an eye, so a wall that is pleasant to sit next to blows out on camera while auto-exposure drops your face into shadow. Dropping it for a call and restoring it afterwards should not need two reflashes.

The value rides along in every frame (5-255) and persists to TinyDB. Moving the slider redraws from the cached prices rather than refetching, so it costs no API request. It fires on release rather than on drag, since each change is a serial write and a full repaint.

Note that dimming the wall is only half of the camera problem: the other half is putting some light on your face, so the two are within a few stops of each other. No camera setting fixes a bright wall in a dark room.

Prices come from Yahoo's chart endpoint, which needs no API key but does need a browser User-Agent header -- without one it answers 429. One call returns both the daily series and the current price. It is an undocumented endpoint, so stock.py keeps a PROVIDERS list and falls back to Finnhub plus Alpha Vantage if it ever changes shape. Note the price is roughly 15 minutes delayed.

A background thread refreshes every minute during market hours and every 15 minutes outside them. It only draws while the wall is actually on stock mode, so selecting another mode parks it rather than fighting for the display. If fetching fails it keeps the last good chart up but flags it, and the wall dims to half brightness rather than presenting stale prices as current.

To iterate on the layout without the hardware:

python3 stock.py NYT
python3 stock.py TSLA PFE GOOGL

Roadmap

  • Water effect
  • Text mode
  • Weather integration
  • 3-color gradient
  • Horizontal gradient
  • Icon/graphic support
  • Rotating stock watchlist

About

Python3 server to communicate to lightwall

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages