write a config module
This commit is contained in:
parent
7149273986
commit
4fc398bb26
11 changed files with 115 additions and 50 deletions
1
.env.sample
Normal file
1
.env.sample
Normal file
|
|
@ -0,0 +1 @@
|
|||
export MQTT_PW=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,3 +2,5 @@
|
|||
*.ez
|
||||
/build
|
||||
erl_crash.dump
|
||||
|
||||
.env
|
||||
|
|
|
|||
|
|
@ -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
5
config.yml
Normal 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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
78
src/config.gleam
Normal 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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
23
weather_portal/.github/workflows/test.yml
vendored
23
weather_portal/.github/workflows/test.yml
vendored
|
|
@ -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
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# weather_portal
|
||||
|
||||
[](https://hex.pm/packages/weather_portal)
|
||||
[](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
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue