Strategies for Self-Hosted Apps?

Hi folks,

I’ve finally had some time to start experimenting with Tidbyt app development, and while it’s a lot of fun, I’m a little disappointed with the story for local/personal app hosting (i.e., there doesn’t seem to be one currently). I thought I’d solicit any advice/suggestions around how folks are handling serving their own apps to their personal Tidbyts.

Context: I’ve written a custom app that combines a clock, calendar, weather (using the met.no API), and some other custom status indications from Home Assistant into a single view (see attached image, with HA info removed). Due to the specific-to-me nature of the app, this isn’t something I’m interested in publishing to the Community, but I’d still like it to serve as my primary (and at this point, only) Tidbyt app for my personal device.

If I just wanted to re-render the app every few seconds and push, I’ve already got a solution for this working on my local machine, but I’d prefer to leverage the pixel serve command in order to cache my API calls with the included cache helper–I don’t want to spam the weather API. However, from what I can tell, curl calls issued to a running pixlet server don’t trigger a new .webp view generation as the server “view” in browser is actually a little Javascript app (which curl cannot execute).

Is there no way around this other than building out a separate service to handle rate-limiting my calls to the met.no weather API, and pulling in that data via a local HTTP call? Is there something I’m missing around forcing a local pixlet server to re-generate the .webp view for device push? Any and all help would be appreciated. Thanks!

I don’t think that’s what the serve function is for at all. If you want a new webp then why not just call pixlet render ?

Are you sure the cache functionality is actually working with your local pixlet install ? I was under the assumption that the cache module only worked when the .star file if run within the pixlet server environment.

To clarify: I realize this is at least not a documented use of the serve function, but I also saw some (older) references to triggering re-renders while it was running rather than using the pixlet render call. Specifically, I was hoping that there was some way to leverage the included cache helpers in a locally hosted app (as I assume the Tidbyt devs are leveraging this when hosting Community apps, given they require reasonable caching). Based on the lack of replies here and some follow-up in the Discord, it sounds like this isn’t currently a thing.

(Also, I may not have been clear, but I never had caching working in a locally served app that I was actually pushing to my Tidbyt—only within the pixlet serve browser-based environment.)

I’ll follow up later when I’ve put something together, but my plan is to build a simple Node.js app that will receive incoming requests from my .star app when rendered and handle the caching itself–either grabbing new data from any APIs I call, or serving up cached data if we’re under whatever rate limit I establish. I’ll then plan to script the render and push of my app to my Tidbyt on some smaller interval (which is simple enough with a bash scripted while loop, and something I already have a proof of concept working for).

If anyone’s interested, here’s a very quick and dirty (and not at all robust) Node.js script I threw together tonight to serve up rate-limited API data every time it’s called (I’m using the OpenWeatherMap API). You’ll have to fill in some of the gaps around the referenced Sequelize includes where I define models and whatnot, but you could just as easily cache data in-memory and eschew the MySQL store stuff I’m using.

const axios = require('axios')
const express = require('express')
const app = express()
const port = 4036

//set up DB models
const {
	Weather,
	Sequelize,
	sequelize
} = require('./sequelize');

const Op = Sequelize.Op;

app.get('/', async (req, res) => {
	try {
		const existingWeather = await Weather.findOne({
			where: {
				createdAt: {
					[Op.gte]: Sequelize.literal("NOW() - INTERVAL 12 MINUTE")
				}
			},
			raw: true
		})

		//no matching record found
		if (existingWeather === null) {
			await Weather.destroy({
				truncate: true
			})

			axios
				.get('https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&units=imperial&appid={API_KEY}')
				.then(apiRes => {
					(async () => {
						const newWeather = await Weather.create({ weather_response: apiRes.data })
						res.send(apiRes.data)
					})()
				})
		} else {
			res.send(existingWeather.weather_response);
		}

	} catch (error) {
		console.log(error)
	}
})

app.listen(port, () => {
	console.log(`Weather server listening on port ${port}`)	
})

I’m running this Node app, and then using a bash script to call pixlet render and then pixlet push for my Pixlet app every few seconds. The Pixlet .star app script is what handles calling the Node app and parsing the received weather data.