Uses NWS data to let you know when you need to water your plants.
It does this by comparing precipitation against evapotranspiration. If the amount of precipitation received is greater than the amount of evapotranspiration, you don't need to water your plants! On the other hand, if precipitation is less than evapotranspiration, that means your plants need a little extra water to make up the deficit.
The NWS only provides this data for the continental US, so this service will not work for other locations. If you know of a good datasource that could be used for any other locations, please let me know. Maybe https://api.met.no/doc/locationforecast/datamodel could be used?
- Collect data from NWS
- TODO: API to expose collected data
- TODO: Minimal frontend (display rolling weekly sums)
- TODO: Email reminders when watering is required
- TODO: Advanced fronted (crop coefficients)
- TODO: Documentation for self-hosting, contributing, etc
- TODO: Open registration (user sign up, email verification, etc)
Evapotranspiration is usually pretty consistent day to day. Conversely, precipitation usually comes in bursts; A lot of rain comes on a stormy day, but most days have no rain at all. Luckily, the soil is able to store some amount of water, which provides a buffer for the water required by plants. This means that daily precipitation and evapotranspiration don't need to match up exactly. However, if evapotranspiration exceeds precipitation for a significant amount of time, the water in the soil will be depleted, and plants will begin to wither.
This app tracks the daily values for precipitation and evapotranspiration, and presents the user with rolling weekly sums. This provides an easy way to decide when watering is appropriate, and how much to water.
Why weekly? No particular reason, it just felt right. I don't actually know what the best interval is, and will probably test out other intervals.
Another thing to keep in mind is that the evapotranspiration data reported by NWS is a forecasted reference evapotranspiration rate. "Forecasted" means it is not based on direct measurements, so it may lack precision. "Reference evapotranspiration" is the amount of transpiration and evaporation that will occur for a reference crop (grass) under ideal conditions (well watered, completely shading the ground). The reference evapotranspiration can be adjusted for different plants by multiplying it with the crop coefficient. However, this is further complicated by the fact that plants have different water requirements at different stages of growth, as well as many other uncontrollable factors.
The key takeaway here is: evapotranspiration data is a crude way to estimate how much water your plants need, and will not be exact. Direct local measurement will always be more accurate and effective (e.g. does the soil feel dry, are the leaves wilting).
- National Digital Forecast Database provides forecasted reference evapotranspiration (FRET) values.
- NWS API provides observed precipitation values.
Simplicity: I'm trying to keep this as simple as possible, for several reasons:
- Constraints breed creativity
- Complexity is evil
- The budget is 0$
Fun: This is a personal project, so I'm not trying to meet any deadlines or SLAs. I may use technologies and/or designs which are suboptimal, just because I want to try them out. There is no higher goal than happiness.
This diagram shows the main parts of the system, as well as the general "data flow".
graph TD;
accTitle: when2water system architecture
accDescr {
A flow chart showing how data moves between and through the
components of the when2water system.
}
loader[[loader daemon]];
database[(database)];
nws[NWS API];
user([user]);
subgraph fly.io?
server[[api server]];
end
backblaze[BackBlaze B2]
nws -- forecast data --> loader;
loader --> database;
database -. copied daily .-> backblaze;
backblaze -. copied daily .-> server;
subgraph internet;
%% this subgraph is really just here to make the diagram
%% order a little nicer
user -- request new location --> server;
server -- read data --> user;
end;
server -- new locations --> loader;
Every day, the loader daemon queries the NWS API for the precipitation
and evapotranspiration forecast for each location in the
tracked_location table. This data is saved into the datapoint
table.
Every day, the database is copied up to the API server. The API server opens the database in read-only mode. Any requests which require storing data (e.g. a user requests a new location to be tracked) will write to a temporary cache. The loader will periodically make a request to the API to load the cached data into the database.
The loader daemon is the only part of the system that ever writes to the database. This design is used to keep things simple and scalable, and allows us to play to the strengths of SQLite. This works because data doesn't need to flow through the system very quickly, since precipitation and evapotranspiration only need to balance over a week or more. Tracking forecast data on a 24 hour period gives us the granularity we need, while still leaving plenty of time for the data to circulate.
The problems that I can foresee with this design mostly revolve around the fact that the API server can't write directly to the database. This introduces the risk of data loss (if the server crashes between a user write and a loader poll) and data conflict/races (two users write conflicting data to two different API servers). I may be able to mitigate this with by introducing some persistence behind the API servers (e.g. redis, litestream) and/or careful design (e.g. make it clear to users that their actions aren't completed immediately, use some sort of CRDT).