write a config module

This commit is contained in:
Travis Shears 2026-03-23 13:05:21 +01:00
parent 7149273986
commit 4fc398bb26
Signed by: travisshears
GPG key ID: CB9BF1910F3F7469
11 changed files with 115 additions and 50 deletions

1
.env.sample Normal file
View file

@ -0,0 +1 @@
export MQTT_PW=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2
.gitignore vendored
View file

@ -2,3 +2,5 @@
*.ez
/build
erl_crash.dump
.env

View file

@ -2,8 +2,6 @@
This is a real-time, neighborhood-facing weather dashboard that streams sensory data from an MQTT broker (Home Assistant) to a web interface.
Gleam code started created following [this guide](https://gleam.run/writing-gleam/).
## Tech Stack Overview
- **Backend:** Gleam (Target: Erlang/OTP)
- **Web Server:** Mist (HTTP & WebSockets)
@ -11,6 +9,10 @@ Gleam code started created following [this guide](https://gleam.run/writing-glea
- **Frontend:** SolidJS (Signals-based reactivity) + Tailwind CSS
- **Infrastructure:** Nomad (Docker-based deployment)
## Resources
- https://learnxinyminutes.com/gleam/
- [Getting started guide](https://gleam.run/writing-gleam/).
## Development

5
config.yml Normal file
View file

@ -0,0 +1,5 @@
mqtt:
host: "192.168.1.11"
user: "homeassistant"
# password comes from env
jwt_key: apuOQZyML+8PixmVDP4nXU4M # loaded from env on prod

View file

@ -14,6 +14,9 @@ version = "1.0.0"
[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
simplifile = ">= 2.4.0 and < 3.0.0"
glaml = ">= 3.0.2 and < 4.0.0"
envoy = ">= 1.1.0 and < 2.0.0"
[dev_dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

View file

@ -2,10 +2,18 @@
# You typically do not need to edit this file
packages = [
{ name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" },
{ name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
{ name = "glaml", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "yamerl"], otp_app = "glaml", source = "hex", outer_checksum = "100CA23F526AB159712A3204D200969571FC43B193736B320C1400D410DEE7AD" },
{ name = "gleam_stdlib", version = "0.70.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86949BF5D1F0E4AC0AB5B06F235D8A5CC11A2DFC33BF22F752156ED61CA7D0FF" },
{ name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" },
{ name = "simplifile", version = "2.4.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "7C18AFA4FED0B4CE1FA5B0B4BAC1FA1744427054EA993565F6F3F82E5453170D" },
{ name = "yamerl", version = "0.10.0", build_tools = ["rebar3"], requirements = [], otp_app = "yamerl", source = "hex", outer_checksum = "346ADB2963F1051DC837A2364E4ACF6EB7D80097C0F53CBDC3046EC8EC4B4E6E" },
]
[requirements]
envoy = { version = ">= 1.1.0 and < 2.0.0" }
glaml = { version = ">= 3.0.2 and < 4.0.0" }
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
simplifile = { version = ">= 2.4.0 and < 3.0.0" }

78
src/config.gleam Normal file
View file

@ -0,0 +1,78 @@
import envoy
import glaml
import gleam/result
import simplifile
pub type Config {
Config(mqtt_host: String, mqtt_user: String, mqtt_pw: String, jwt_key: String)
}
fn load_file() -> Result(String, String) {
simplifile.read("config.yml")
|> result.map_error(fn(_) { "Failed to read config.yml" })
}
fn parse_yaml(contents: String) -> Result(glaml.Document, String) {
use docs <- result.try(
glaml.parse_string(contents)
|> result.map_error(fn(_) { "Failed to parse config.yml" }),
)
case docs {
[doc] -> Ok(doc)
_ -> Error("Expected exactly one YAML document in config.yml")
}
}
fn compile_config(doc: glaml.Document) -> Result(Config, String) {
let root = glaml.document_root(doc)
// Extract values from YAML
use mqtt_host_result <- result.try(
glaml.select_sugar(root, "mqtt.host")
|> result.map_error(fn(_) { "mqtt.host not found in config.yml" }),
)
use mqtt_user_result <- result.try(
glaml.select_sugar(root, "mqtt.user")
|> result.map_error(fn(_) { "mqtt.user not found in config.yml" }),
)
use jwt_key_result <- result.try(
glaml.select_sugar(root, "jwt_key")
|> result.map_error(fn(_) { "jwt_key not found in config.yml" }),
)
// Extract strings from nodes
case mqtt_host_result, mqtt_user_result, jwt_key_result {
glaml.NodeStr(host), glaml.NodeStr(user), glaml.NodeStr(key) -> {
let yaml_config =
Config(
mqtt_host: host,
mqtt_user: user,
mqtt_pw: "placeholder",
jwt_key: key,
)
// Override with env vars
let final_config =
Config(
mqtt_host: envoy.get("MQTT_HOST")
|> result.unwrap(yaml_config.mqtt_host),
mqtt_user: envoy.get("MQTT_USER")
|> result.unwrap(yaml_config.mqtt_user),
mqtt_pw: envoy.get("MQTT_PW")
|> result.unwrap(yaml_config.mqtt_pw),
jwt_key: envoy.get("JWT_KEY")
|> result.unwrap(yaml_config.jwt_key),
)
Ok(final_config)
}
_, _, _ -> Error("Config values must be strings in config.yml")
}
}
pub fn load_config() -> Result(Config, String) {
use file_content <- result.try(load_file())
use doc <- result.try(parse_yaml(file_content))
compile_config(doc)
}

View file

@ -1,5 +1,17 @@
import config
import gleam/io
pub fn main() -> Nil {
io.println("Hello from weather_portal!")
case config.load_config() {
Ok(cfg) -> {
io.println("Config loaded successfully!")
io.println("MQTT Host: " <> cfg.mqtt_host)
io.println("MQTT User: " <> cfg.mqtt_user)
io.println("MQTT PW: " <> cfg.mqtt_pw)
io.println("JWT Key: " <> cfg.jwt_key)
}
Error(err) -> {
io.println("Failed to load config: " <> err)
}
}
}

View file

@ -7,5 +7,6 @@ task: hookup a subdomain via reverse proxy
-----------------------
DONE task: write a config module
DONE task: install gleam
DONE task: get hello world working on local

View file

@ -1,23 +0,0 @@
name: test
on:
push:
branches:
- master
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: erlef/setup-beam@v1
with:
otp-version: "28"
gleam-version: "1.15.2"
rebar3-version: "3"
# elixir-version: "1"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test

View file

@ -1,24 +0,0 @@
# weather_portal
[![Package Version](https://img.shields.io/hexpm/v/weather_portal)](https://hex.pm/packages/weather_portal)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/weather_portal/)
```sh
gleam add weather_portal@1
```
```gleam
import weather_portal
pub fn main() -> Nil {
// TODO: An example of the project in use
}
```
Further documentation can be found at <https://hexdocs.pm/weather_portal>.
## Development
```sh
gleam run # Run the project
gleam test # Run the tests
```