Compare commits
2 Commits
b11b974dad
...
edee032b91
| Author | SHA1 | Date | |
|---|---|---|---|
|
edee032b91
|
|||
|
8bb17e1440
|
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[env]
|
||||||
|
RUST_LOG = "automation=debug"
|
||||||
@@ -34,9 +34,9 @@ jobs:
|
|||||||
--name automation_rs \
|
--name automation_rs \
|
||||||
--network mqtt \
|
--network mqtt \
|
||||||
-e RUST_LOG=automation=debug \
|
-e RUST_LOG=automation=debug \
|
||||||
-e MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
-e AUTOMATION__SECRETS__MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
||||||
-e HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
-e AUTOMATION__SECRETS__HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
||||||
-e NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
-e AUTOMATION__SECRETS__NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
||||||
git.huizinga.dev/dreaded_x/automation_rs@${{ needs.build.outputs.digest }}
|
git.huizinga.dev/dreaded_x/automation_rs@${{ needs.build.outputs.digest }}
|
||||||
|
|
||||||
docker network connect web automation_rs
|
docker network connect web automation_rs
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
automation.toml
|
||||||
|
|||||||
75
Cargo.lock
generated
75
Cargo.lock
generated
@@ -95,6 +95,7 @@ dependencies = [
|
|||||||
"automation_devices",
|
"automation_devices",
|
||||||
"automation_lib",
|
"automation_lib",
|
||||||
"axum",
|
"axum",
|
||||||
|
"config",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"google_home",
|
"google_home",
|
||||||
"hostname",
|
"hostname",
|
||||||
@@ -327,6 +328,19 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "config"
|
||||||
|
version = "0.15.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0faa974509d38b33ff89282db9c3295707ccf031727c0de9772038ec526852ba"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"pathdiff",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -466,7 +480,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1262,6 +1276,12 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathdiff"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -1409,7 +1429,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1586,7 +1606,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1784,6 +1804,15 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -2035,6 +2064,37 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -2491,6 +2551,15 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winsafe"
|
name = "winsafe"
|
||||||
version = "0.0.19"
|
version = "0.0.19"
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ async-trait = { workspace = true }
|
|||||||
automation_devices = { workspace = true }
|
automation_devices = { workspace = true }
|
||||||
automation_lib = { workspace = true }
|
automation_lib = { workspace = true }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
|
config = { version = "0.15.15", default-features = false, features = [
|
||||||
|
"async",
|
||||||
|
"toml",
|
||||||
|
] }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
google_home = { workspace = true }
|
google_home = { workspace = true }
|
||||||
hostname = { workspace = true }
|
hostname = { workspace = true }
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ RUN cargo auditable build --release
|
|||||||
|
|
||||||
FROM gcr.io/distroless/cc-debian12:nonroot AS runtime
|
FROM gcr.io/distroless/cc-debian12:nonroot AS runtime
|
||||||
COPY --from=builder /app/target/release/automation /app/automation
|
COPY --from=builder /app/target/release/automation /app/automation
|
||||||
ENV AUTOMATION_CONFIG=/app/config.lua
|
ENV AUTOMATION__ENTRYPOINT=/app/config.lua
|
||||||
COPY ./config.lua /app/config.lua
|
COPY ./config.lua /app/config.lua
|
||||||
CMD [ "/app/automation" ]
|
CMD [ "/app/automation" ]
|
||||||
|
|||||||
13
config.lua
13
config.lua
@@ -1,16 +1,13 @@
|
|||||||
local device_manager = require("device_manager")
|
local device_manager = require("device_manager")
|
||||||
local utils = require("utils")
|
local utils = require("utils")
|
||||||
|
local secrets = require("secrets")
|
||||||
|
local debug = require("variables").debug or false
|
||||||
|
|
||||||
print(_VERSION)
|
print(_VERSION)
|
||||||
|
|
||||||
local host = utils.get_hostname()
|
local host = utils.get_hostname()
|
||||||
print("Running @" .. host)
|
print("Running @" .. host)
|
||||||
|
|
||||||
local debug, value = pcall(utils.get_env, "DEBUG")
|
|
||||||
if debug and value ~= "true" then
|
|
||||||
debug = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function mqtt_z2m(topic)
|
local function mqtt_z2m(topic)
|
||||||
return "zigbee2mqtt/" .. topic
|
return "zigbee2mqtt/" .. topic
|
||||||
end
|
end
|
||||||
@@ -28,12 +25,12 @@ local mqtt_client = require("mqtt").new({
|
|||||||
port = 8883,
|
port = 8883,
|
||||||
client_name = "automation-" .. host,
|
client_name = "automation-" .. host,
|
||||||
username = "mqtt",
|
username = "mqtt",
|
||||||
password = utils.get_env("MQTT_PASSWORD"),
|
password = secrets.mqtt_password,
|
||||||
tls = host == "zeus" or host == "hephaestus",
|
tls = host == "zeus" or host == "hephaestus",
|
||||||
})
|
})
|
||||||
|
|
||||||
local ntfy = Ntfy.new({
|
local ntfy = Ntfy.new({
|
||||||
topic = utils.get_env("NTFY_TOPIC"),
|
topic = secrets.ntfy_topic,
|
||||||
})
|
})
|
||||||
device_manager:add(ntfy)
|
device_manager:add(ntfy)
|
||||||
|
|
||||||
@@ -147,7 +144,7 @@ on_light:add(function(light)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
local hue_ip = "10.0.0.102"
|
local hue_ip = "10.0.0.102"
|
||||||
local hue_token = utils.get_env("HUE_TOKEN")
|
local hue_token = secrets.hue_token
|
||||||
|
|
||||||
local hue_bridge = HueBridge.new({
|
local hue_bridge = HueBridge.new({
|
||||||
identifier = "hue_bridge",
|
identifier = "hue_bridge",
|
||||||
|
|||||||
17
src/config.rs
Normal file
17
src/config.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(default = "default_entrypoint")]
|
||||||
|
pub entrypoint: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub variables: HashMap<String, String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub secrets: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_entrypoint() -> String {
|
||||||
|
"./config.lua".into()
|
||||||
|
}
|
||||||
37
src/main.rs
37
src/main.rs
@@ -1,4 +1,6 @@
|
|||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
|
mod config;
|
||||||
|
mod secret;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@@ -6,6 +8,7 @@ use std::path::Path;
|
|||||||
use std::process;
|
use std::process;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use ::config::{Environment, File};
|
||||||
use automation_lib::config::{FulfillmentConfig, MqttConfig};
|
use automation_lib::config::{FulfillmentConfig, MqttConfig};
|
||||||
use automation_lib::device_manager::DeviceManager;
|
use automation_lib::device_manager::DeviceManager;
|
||||||
use automation_lib::helpers;
|
use automation_lib::helpers;
|
||||||
@@ -14,6 +17,7 @@ use axum::extract::{FromRef, State};
|
|||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::routing::post;
|
use axum::routing::post;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
|
use config::Config;
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use google_home::{GoogleHome, Request, Response};
|
use google_home::{GoogleHome, Request, Response};
|
||||||
use mlua::LuaSerdeExt;
|
use mlua::LuaSerdeExt;
|
||||||
@@ -22,6 +26,8 @@ use tokio::net::TcpListener;
|
|||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use web::{ApiError, User};
|
use web::{ApiError, User};
|
||||||
|
|
||||||
|
use crate::secret::EnvironmentSecretFile;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
pub openid_url: String,
|
pub openid_url: String,
|
||||||
@@ -69,7 +75,21 @@ async fn app() -> anyhow::Result<()> {
|
|||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
// console_subscriber::init();
|
|
||||||
|
let config: Config = ::config::Config::builder()
|
||||||
|
.add_source(
|
||||||
|
File::with_name(&format!("{}.toml", std::env!("CARGO_PKG_NAME"))).required(false),
|
||||||
|
)
|
||||||
|
.add_source(
|
||||||
|
Environment::default()
|
||||||
|
.prefix(std::env!("CARGO_PKG_NAME"))
|
||||||
|
.separator("__"),
|
||||||
|
)
|
||||||
|
.add_source(EnvironmentSecretFile::default())
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.try_deserialize()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
info!("Starting automation_rs...");
|
info!("Starting automation_rs...");
|
||||||
|
|
||||||
@@ -133,11 +153,10 @@ async fn app() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
lua.register_module("device_manager", device_manager.clone())?;
|
lua.register_module("device_manager", device_manager.clone())?;
|
||||||
|
|
||||||
|
lua.register_module("variables", lua.to_value(&config.variables)?)?;
|
||||||
|
lua.register_module("secrets", lua.to_value(&config.secrets)?)?;
|
||||||
|
|
||||||
let utils = lua.create_table()?;
|
let utils = lua.create_table()?;
|
||||||
let get_env = lua.create_function(|_lua, name: String| {
|
|
||||||
std::env::var(name).map_err(mlua::ExternalError::into_lua_err)
|
|
||||||
})?;
|
|
||||||
utils.set("get_env", get_env)?;
|
|
||||||
let get_hostname = lua.create_function(|_lua, ()| {
|
let get_hostname = lua.create_function(|_lua, ()| {
|
||||||
hostname::get()
|
hostname::get()
|
||||||
.map(|name| name.to_str().unwrap_or("unknown").to_owned())
|
.map(|name| name.to_str().unwrap_or("unknown").to_owned())
|
||||||
@@ -151,17 +170,13 @@ async fn app() -> anyhow::Result<()> {
|
|||||||
.as_millis())
|
.as_millis())
|
||||||
})?;
|
})?;
|
||||||
utils.set("get_epoch", get_epoch)?;
|
utils.set("get_epoch", get_epoch)?;
|
||||||
|
|
||||||
lua.register_module("utils", utils)?;
|
lua.register_module("utils", utils)?;
|
||||||
|
|
||||||
automation_devices::register_with_lua(&lua)?;
|
automation_devices::register_with_lua(&lua)?;
|
||||||
helpers::register_with_lua(&lua)?;
|
helpers::register_with_lua(&lua)?;
|
||||||
|
|
||||||
// TODO: Make this not hardcoded
|
let entrypoint = Path::new(&config.entrypoint);
|
||||||
let config_filename = std::env::var("AUTOMATION_CONFIG").unwrap_or("./config.lua".into());
|
let fulfillment_config: mlua::Value = lua.load(entrypoint).eval_async().await?;
|
||||||
let config_path = Path::new(&config_filename);
|
|
||||||
|
|
||||||
let fulfillment_config: mlua::Value = lua.load(config_path).eval_async().await?;
|
|
||||||
let fulfillment_config: FulfillmentConfig = lua.from_value(fulfillment_config)?;
|
let fulfillment_config: FulfillmentConfig = lua.from_value(fulfillment_config)?;
|
||||||
|
|
||||||
// Create google home fulfillment route
|
// Create google home fulfillment route
|
||||||
|
|||||||
43
src/secret.rs
Normal file
43
src/secret.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use std::str::from_utf8;
|
||||||
|
|
||||||
|
use config::{ConfigError, Source, Value, ValueKind};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct EnvironmentSecretFile {}
|
||||||
|
|
||||||
|
const SUFFIX: &str = "__file";
|
||||||
|
const PREFIX: &str = concat!(std::env!("CARGO_PKG_NAME"), "__");
|
||||||
|
|
||||||
|
impl Source for EnvironmentSecretFile {
|
||||||
|
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&self) -> Result<config::Map<String, config::Value>, ConfigError> {
|
||||||
|
Ok(std::env::vars()
|
||||||
|
.flat_map(|(key, value): (String, String)| {
|
||||||
|
let key = key.to_lowercase();
|
||||||
|
if !key.starts_with(PREFIX) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !key.ends_with(SUFFIX) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let suffix_length = key.len() - SUFFIX.len();
|
||||||
|
let key = key[PREFIX.len()..suffix_length].replace("__", ".");
|
||||||
|
|
||||||
|
if key.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = from_utf8(&std::fs::read(&value).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
Some((key, Value::new(Some(&value), ValueKind::String(content))))
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user