Compare commits
7 Commits
b24bb70777
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
77a32d1507
|
|||
|
ce302b148e
|
|||
|
fe6b3b52e1
|
|||
|
361d799377
|
|||
|
d353fa9759
|
|||
|
9979ab3446
|
|||
|
b66357749b
|
@@ -1,2 +1,5 @@
|
|||||||
[env]
|
[env]
|
||||||
RUST_LOG = "automation=debug"
|
RUST_LOG = "automation=debug"
|
||||||
|
|
||||||
|
[target.x86_64-unknown-linux-musl]
|
||||||
|
rustflags = ["-C", "link-arg=-lc"]
|
||||||
|
|||||||
@@ -2,3 +2,5 @@
|
|||||||
.env
|
.env
|
||||||
# Use the rust environment provided by the container
|
# Use the rust environment provided by the container
|
||||||
rust-toolchain.toml
|
rust-toolchain.toml
|
||||||
|
Dockerfile
|
||||||
|
docker-bake.hcl
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ jobs:
|
|||||||
docker stop automation_rs || true
|
docker stop automation_rs || true
|
||||||
docker rm automation_rs || true
|
docker rm automation_rs || true
|
||||||
|
|
||||||
|
- name: Login to registry
|
||||||
|
run: |
|
||||||
|
docker login git.huizinga.dev -u ${{ gitea.actor }} -p ${{ secrets.REGISTRY_TOKEN }} \
|
||||||
|
|
||||||
- name: Create container
|
- name: Create container
|
||||||
run: |
|
run: |
|
||||||
docker create \
|
docker create \
|
||||||
@@ -37,6 +41,8 @@ jobs:
|
|||||||
-e AUTOMATION__SECRETS__MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
-e AUTOMATION__SECRETS__MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
||||||
-e AUTOMATION__SECRETS__HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
-e AUTOMATION__SECRETS__HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
||||||
-e AUTOMATION__SECRETS__NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
-e AUTOMATION__SECRETS__NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
||||||
|
-e AUTOMATION__SECRETS__PRINTER_DEVICE_ID=${{ secrets.PRINTER_DEVICE_ID }} \
|
||||||
|
-e AUTOMATION__SECRETS__PRINTER_ACCESS_CODE=${{ secrets.PRINTER_ACCESS_CODE }} \
|
||||||
$(echo ${{ toJSON(needs.build.outputs.images) }} | jq .automation -r)
|
$(echo ${{ toJSON(needs.build.outputs.images) }} | jq .automation -r)
|
||||||
|
|
||||||
docker network connect web automation_rs
|
docker network connect web automation_rs
|
||||||
|
|||||||
Generated
+1045
-575
File diff suppressed because it is too large
Load Diff
+21
-21
@@ -16,28 +16,28 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
air_filter_types = { git = "https://git.huizinga.dev/Dreaded_X/airfilter", tag = "v0.4.4" }
|
air_filter_types = { git = "https://git.huizinga.dev/Dreaded_X/airfilter", tag = "v0.4.4" }
|
||||||
anyhow = "1.0.99"
|
anyhow = "1.0.102"
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
automation_cast = { path = "./automation_cast" }
|
automation_cast = { path = "./automation_cast" }
|
||||||
automation_devices = { path = "./automation_devices" }
|
automation_devices = { path = "./automation_devices" }
|
||||||
automation_lib = { path = "./automation_lib" }
|
automation_lib = { path = "./automation_lib" }
|
||||||
automation_macro = { path = "./automation_macro" }
|
automation_macro = { path = "./automation_macro" }
|
||||||
axum = "0.8.4"
|
axum = "0.8.9"
|
||||||
bytes = "1.10.1"
|
bytes = "1.11.1"
|
||||||
dyn-clone = "1.0.20"
|
dyn-clone = "1.0.20"
|
||||||
eui48 = { version = "1.1.0", features = [
|
eui48 = { version = "1.1.0", features = [
|
||||||
"disp_hexstring",
|
"disp_hexstring",
|
||||||
"serde",
|
"serde",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
futures = "0.3.31"
|
futures = "0.3.32"
|
||||||
google_home = { path = "./google_home/google_home" }
|
google_home = { path = "./google_home/google_home" }
|
||||||
google_home_macro = { path = "./google_home/google_home_macro" }
|
google_home_macro = { path = "./google_home/google_home_macro" }
|
||||||
hostname = "0.4.1"
|
hostname = "0.4.2"
|
||||||
inventory = "0.3.21"
|
inventory = "0.3.24"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
json_value_merge = "2.0.1"
|
json_value_merge = "2.0.1"
|
||||||
lua_typed = { git = "https://git.huizinga.dev/Dreaded_X/lua_typed" }
|
lua_typed = { git = "https://git.huizinga.dev/Dreaded_X/lua_typed" }
|
||||||
mlua = { version = "0.11.3", features = [
|
mlua = { version = "0.11.6", features = [
|
||||||
"lua54",
|
"lua54",
|
||||||
"vendored",
|
"vendored",
|
||||||
"macros",
|
"macros",
|
||||||
@@ -45,23 +45,23 @@ mlua = { version = "0.11.3", features = [
|
|||||||
"async",
|
"async",
|
||||||
"send",
|
"send",
|
||||||
] }
|
] }
|
||||||
proc-macro2 = "1.0.101"
|
proc-macro2 = "1.0.106"
|
||||||
quote = "1.0.40"
|
quote = "1.0.45"
|
||||||
reqwest = { version = "0.12.23", features = [
|
reqwest = { version = "0.13.3", features = [
|
||||||
"json",
|
"json",
|
||||||
"rustls-tls",
|
"rustls",
|
||||||
], default-features = false } # Use rustls, since the other packages also use rustls
|
], default-features = false } # Use rustls, since the other packages also use rustls
|
||||||
rumqttc = "0.24.0"
|
rumqttc = "0.25.1"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.143"
|
serde_json = "1.0.149"
|
||||||
serde_repr = "0.1.20"
|
serde_repr = "0.1.20"
|
||||||
syn = { version = "2.0.106" }
|
syn = { version = "2.0.117" }
|
||||||
thiserror = "2.0.16"
|
thiserror = "2.0.18"
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||||
tokio-cron-scheduler = "0.15.0"
|
tokio-cron-scheduler = "0.15.1"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.20"
|
tracing-subscriber = "0.3.23"
|
||||||
wakey = "0.3.0"
|
wakey = "0.4.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
@@ -70,7 +70,7 @@ automation_devices = { workspace = true }
|
|||||||
automation_lib = { workspace = true }
|
automation_lib = { workspace = true }
|
||||||
automation_macro = { path = "./automation_macro" }
|
automation_macro = { path = "./automation_macro" }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
config = { version = "0.15.15", default-features = false, features = [
|
config = { version = "0.15.22", default-features = false, features = [
|
||||||
"async",
|
"async",
|
||||||
"toml",
|
"toml",
|
||||||
] }
|
] }
|
||||||
|
|||||||
+4
-4
@@ -1,9 +1,7 @@
|
|||||||
FROM rust:1.89 AS base
|
FROM rust:1.95-alpine AS base
|
||||||
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
|
|
||||||
RUN cargo install cargo-chef --locked --version 0.1.71 && \
|
RUN cargo install cargo-chef --locked --version 0.1.71 && \
|
||||||
cargo install cargo-auditable --locked --version 0.6.6
|
cargo install cargo-auditable --locked --version 0.6.6
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY ./rust-toolchain.toml .
|
|
||||||
RUN rustup toolchain install
|
RUN rustup toolchain install
|
||||||
|
|
||||||
FROM base AS planner
|
FROM base AS planner
|
||||||
@@ -11,6 +9,7 @@ COPY . .
|
|||||||
RUN cargo chef prepare --recipe-path recipe.json
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
|
RUN apk add --no-cache g++=15.2.0-r2 cmake=4.1.3-r0 make=4.4.1-r3 openssl-dev=3.5.7-r0 openssl-libs-static=3.5.7-r0
|
||||||
# HACK: Now we can use unstable feature while on stable rust!
|
# HACK: Now we can use unstable feature while on stable rust!
|
||||||
ENV RUSTC_BOOTSTRAP=1
|
ENV RUSTC_BOOTSTRAP=1
|
||||||
COPY --from=planner /app/recipe.json recipe.json
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
@@ -21,7 +20,8 @@ ARG RELEASE_VERSION
|
|||||||
ENV RELEASE_VERSION=${RELEASE_VERSION}
|
ENV RELEASE_VERSION=${RELEASE_VERSION}
|
||||||
RUN cargo auditable build --release
|
RUN cargo auditable build --release
|
||||||
|
|
||||||
FROM gcr.io/distroless/cc-debian12:nonroot AS runtime
|
|
||||||
|
FROM gcr.io/distroless/static-debian13:nonroot AS runtime
|
||||||
COPY --from=builder /app/target/release/automation /app/automation
|
COPY --from=builder /app/target/release/automation /app/automation
|
||||||
ENV AUTOMATION__ENTRYPOINT=/app/config/config.lua
|
ENV AUTOMATION__ENTRYPOINT=/app/config/config.lua
|
||||||
ENV LUA_PATH="/app/?.lua;;"
|
ENV LUA_PATH="/app/?.lua;;"
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ thiserror = { workspace = true }
|
|||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
wakey = { workspace = true }
|
wakey = { workspace = true }
|
||||||
|
bambulab = { version = "0.4.30", default-features = false }
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
use std::convert::Infallible;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::action_callback::ActionCallback;
|
||||||
|
use automation_lib::device::Device;
|
||||||
|
use automation_macro::{Device, LuaDeviceConfig};
|
||||||
|
use bambulab::client::Client;
|
||||||
|
use bambulab::{Command, Message};
|
||||||
|
use google_home::errors::{self};
|
||||||
|
use google_home::traits::OnOff;
|
||||||
|
use lua_typed::Typed;
|
||||||
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
|
use crate::{DebugWrap, LuaDeviceCreate};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, LuaDeviceConfig, Typed, Default)]
|
||||||
|
#[typed(as = "BambuCallbacks")]
|
||||||
|
pub struct Callbacks {
|
||||||
|
#[device_config(from_lua, default)]
|
||||||
|
#[typed(default)]
|
||||||
|
pub state: ActionCallback<Bambu>,
|
||||||
|
#[device_config(from_lua, default)]
|
||||||
|
#[typed(default)]
|
||||||
|
pub connected: ActionCallback<Bambu>,
|
||||||
|
}
|
||||||
|
crate::register_type!(Callbacks);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, LuaDeviceConfig, Typed)]
|
||||||
|
#[typed(as = "BambuConfig")]
|
||||||
|
pub struct Config {
|
||||||
|
pub host: String,
|
||||||
|
pub device_id: String,
|
||||||
|
pub access_code: String,
|
||||||
|
#[device_config(from_lua, default)]
|
||||||
|
pub callbacks: Callbacks,
|
||||||
|
}
|
||||||
|
crate::register_type!(Config);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Device)]
|
||||||
|
#[device(traits(OnOff))]
|
||||||
|
pub struct Bambu {
|
||||||
|
config: Config,
|
||||||
|
|
||||||
|
client: DebugWrap<Client>,
|
||||||
|
|
||||||
|
state: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
crate::register_device!(Bambu);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LuaDeviceCreate for Bambu {
|
||||||
|
type Config = Config;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Infallible> {
|
||||||
|
trace!(id = config.device_id, "Setting up bambu");
|
||||||
|
|
||||||
|
let (tx, mut rx) = tokio::sync::broadcast::channel(25);
|
||||||
|
let client = Client::new(&config.host, &config.access_code, &config.device_id, tx);
|
||||||
|
|
||||||
|
let state = Arc::new(AtomicBool::new(false));
|
||||||
|
let bambu = Self {
|
||||||
|
config,
|
||||||
|
client: DebugWrap(client.clone()),
|
||||||
|
state: state.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
tokio::spawn({
|
||||||
|
let mut bambu = bambu.clone();
|
||||||
|
async move {
|
||||||
|
// The printer might be offline so periodically try to reconnecct
|
||||||
|
loop {
|
||||||
|
bambu.client.run().await.ok();
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tokio::spawn({
|
||||||
|
let bambu = bambu.clone();
|
||||||
|
async move {
|
||||||
|
loop {
|
||||||
|
let message = rx.recv().await.unwrap();
|
||||||
|
|
||||||
|
match message {
|
||||||
|
Message::Print(data) => 'print: {
|
||||||
|
// Extract the state of the chamber light
|
||||||
|
let Some(light_report) = data.print.lights_report else {
|
||||||
|
break 'print;
|
||||||
|
};
|
||||||
|
|
||||||
|
let on = light_report
|
||||||
|
.iter()
|
||||||
|
.find(|report| report.node == "chamber_light")
|
||||||
|
.map(|report| report.mode == "on")
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
state.store(on, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
bambu.config.callbacks.state.call(bambu.clone()).await;
|
||||||
|
}
|
||||||
|
Message::Connected => {
|
||||||
|
debug!(id = bambu.config.device_id, "Connected");
|
||||||
|
client.publish(Command::PushAll).await.unwrap();
|
||||||
|
|
||||||
|
bambu.config.callbacks.connected.call(bambu.clone()).await;
|
||||||
|
}
|
||||||
|
// Ignore everything else
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(bambu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for Bambu {
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
self.config.device_id.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OnOff for Bambu {
|
||||||
|
async fn on(&self) -> Result<bool, errors::ErrorCode> {
|
||||||
|
Ok(self.state.load(std::sync::atomic::Ordering::Relaxed))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_on(&self, on: bool) -> Result<(), errors::ErrorCode> {
|
||||||
|
// NOTE: This will error in case the printer is offline, but we don't really care in that
|
||||||
|
// case so we just ignore the error
|
||||||
|
self.client.publish(Command::SetChamberLight(on)).await.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -122,16 +122,12 @@ impl OnMqtt for HueSwitch {
|
|||||||
Action::LeftHold => self.config.left_hold_callback.call(self.clone()).await,
|
Action::LeftHold => self.config.left_hold_callback.call(self.clone()).await,
|
||||||
Action::RightHold => self.config.right_hold_callback.call(self.clone()).await,
|
Action::RightHold => self.config.right_hold_callback.call(self.clone()).await,
|
||||||
// If there is no hold action, the switch will act like a normal release
|
// If there is no hold action, the switch will act like a normal release
|
||||||
Action::RightHoldRelease => {
|
Action::RightHoldRelease if self.config.right_hold_callback.is_empty() => {
|
||||||
if self.config.right_hold_callback.is_empty() {
|
|
||||||
self.config.right_callback.call(self.clone()).await
|
self.config.right_callback.call(self.clone()).await
|
||||||
}
|
}
|
||||||
}
|
Action::LeftHoldRelease if self.config.left_hold_callback.is_empty() => {
|
||||||
Action::LeftHoldRelease => {
|
|
||||||
if self.config.left_hold_callback.is_empty() {
|
|
||||||
self.config.left_callback.call(self.clone()).await
|
self.config.left_callback.call(self.clone()).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#![feature(debug_closure_helpers)]
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
mod air_filter;
|
mod air_filter;
|
||||||
|
mod bambu;
|
||||||
mod contact_sensor;
|
mod contact_sensor;
|
||||||
mod hue_bridge;
|
mod hue_bridge;
|
||||||
mod hue_group;
|
mod hue_group;
|
||||||
@@ -13,6 +15,9 @@ mod wake_on_lan;
|
|||||||
mod washer;
|
mod washer;
|
||||||
mod zigbee;
|
mod zigbee;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use automation_lib::Module;
|
use automation_lib::Module;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
@@ -20,6 +25,31 @@ use tracing::{debug, warn};
|
|||||||
type DeviceNameFn = fn() -> String;
|
type DeviceNameFn = fn() -> String;
|
||||||
type RegisterDeviceFn = fn(lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
|
type RegisterDeviceFn = fn(lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DebugWrap<T: Clone>(T);
|
||||||
|
|
||||||
|
impl<T: Clone> DerefMut for DebugWrap<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Deref for DebugWrap<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> fmt::Debug for DebugWrap<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("DebugWrap")
|
||||||
|
.field_with(|f| f.write_str(stringify!(T)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RegisteredDevice {
|
pub struct RegisteredDevice {
|
||||||
name_fn: DeviceNameFn,
|
name_fn: DeviceNameFn,
|
||||||
register_fn: RegisterDeviceFn,
|
register_fn: RegisterDeviceFn,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#![feature(iter_intersperse)]
|
|
||||||
#![feature(iterator_try_collect)]
|
#![feature(iterator_try_collect)]
|
||||||
mod device;
|
mod device;
|
||||||
mod lua_device_config;
|
mod lua_device_config;
|
||||||
|
|||||||
+2
-2
@@ -10,12 +10,12 @@ return {
|
|||||||
openid_url = "https://login.huizinga.dev/api/oidc",
|
openid_url = "https://login.huizinga.dev/api/oidc",
|
||||||
},
|
},
|
||||||
mqtt = {
|
mqtt = {
|
||||||
host = ((host == "zeus" or host == "hephaestus") and "olympus.lan.huizinga.dev") or "mosquitto",
|
host = ((host == "zeus" or host == "hephaestus") and "olympus.huizinga.lan") or "mosquitto",
|
||||||
port = 8883,
|
port = 8883,
|
||||||
client_name = "automation-" .. host,
|
client_name = "automation-" .. host,
|
||||||
username = "mqtt",
|
username = "mqtt",
|
||||||
password = secrets.mqtt_password,
|
password = secrets.mqtt_password,
|
||||||
tls = host == "zeus" or host == "hephaestus",
|
tls = false,
|
||||||
},
|
},
|
||||||
modules = {
|
modules = {
|
||||||
require("config.battery"),
|
require("config.battery"),
|
||||||
|
|||||||
@@ -4,15 +4,35 @@ local helper = require("config.helper")
|
|||||||
local presence = require("config.presence")
|
local presence = require("config.presence")
|
||||||
local windows = require("config.windows")
|
local windows = require("config.windows")
|
||||||
|
|
||||||
|
local secrets = require("automation:secrets")
|
||||||
|
|
||||||
--- @type Module
|
--- @type Module
|
||||||
local module = {}
|
local module = {}
|
||||||
|
|
||||||
function module.setup(mqtt_client)
|
function module.setup(mqtt_client)
|
||||||
local light = devices.LightOnOff.new({
|
local light = nil
|
||||||
|
|
||||||
|
local bambu = devices.Bambu.new({
|
||||||
|
host = "10.0.0.108",
|
||||||
|
device_id = secrets.printer_device_id,
|
||||||
|
access_code = secrets.printer_access_code,
|
||||||
|
callbacks = {
|
||||||
|
connected = function(self)
|
||||||
|
if light ~= nil then
|
||||||
|
self:set_on(light:on())
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
light = devices.LightOnOff.new({
|
||||||
name = "Light",
|
name = "Light",
|
||||||
room = "Guest Room",
|
room = "Guest Room",
|
||||||
topic = helper.mqtt_z2m("guest/light"),
|
topic = helper.mqtt_z2m("guest/light"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
|
callback = function(_, state)
|
||||||
|
bambu:set_on(state.state)
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
presence.turn_off_when_away(light)
|
presence.turn_off_when_away(light)
|
||||||
|
|
||||||
@@ -37,6 +57,7 @@ function module.setup(mqtt_client)
|
|||||||
light,
|
light,
|
||||||
window,
|
window,
|
||||||
printer,
|
printer,
|
||||||
|
bambu,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,25 @@ function devices.AirFilter.new(config) end
|
|||||||
---@field url string
|
---@field url string
|
||||||
local AirFilterConfig
|
local AirFilterConfig
|
||||||
|
|
||||||
|
---@class Bambu: DeviceInterface, OnOffInterface
|
||||||
|
local Bambu
|
||||||
|
devices.Bambu = {}
|
||||||
|
---@param config BambuConfig
|
||||||
|
---@return Bambu
|
||||||
|
function devices.Bambu.new(config) end
|
||||||
|
|
||||||
|
---@class BambuCallbacks
|
||||||
|
---@field state (fun(_: Bambu) | fun(_: Bambu)[])?
|
||||||
|
---@field connected (fun(_: Bambu) | fun(_: Bambu)[])?
|
||||||
|
local BambuCallbacks
|
||||||
|
|
||||||
|
---@class BambuConfig
|
||||||
|
---@field host string
|
||||||
|
---@field device_id string
|
||||||
|
---@field access_code string
|
||||||
|
---@field callbacks BambuCallbacks
|
||||||
|
local BambuConfig
|
||||||
|
|
||||||
---@class ConfigLightLightStateBrightness
|
---@class ConfigLightLightStateBrightness
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field room (string)?
|
---@field room (string)?
|
||||||
|
|||||||
+3
-1
@@ -5,7 +5,9 @@ group "default" {
|
|||||||
targets = ["automation"]
|
targets = ["automation"]
|
||||||
}
|
}
|
||||||
|
|
||||||
target "docker-metadata-action" {}
|
target "docker-metadata-action" {
|
||||||
|
tags = []
|
||||||
|
}
|
||||||
|
|
||||||
target "automation" {
|
target "automation" {
|
||||||
inherits = ["docker-metadata-action"]
|
inherits = ["docker-metadata-action"]
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2025-08-20"
|
channel = "nightly-2026-05-12"
|
||||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![feature(if_let_guard)]
|
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod schedule;
|
pub mod schedule;
|
||||||
pub mod secret;
|
pub mod secret;
|
||||||
|
|||||||
Reference in New Issue
Block a user