init
This commit is contained in:
commit
34b8807b6d
6 changed files with 1190 additions and 0 deletions
181
src/main.rs
Normal file
181
src/main.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use log::{info, warn};
|
||||
use std::{
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use axum::{
|
||||
Json, Router,
|
||||
extract::State,
|
||||
response::Html,
|
||||
routing::{get, post},
|
||||
};
|
||||
use rumqttc::{AsyncClient, MqttOptions, QoS};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::task;
|
||||
|
||||
struct Lights {
|
||||
on: AtomicBool,
|
||||
brightness: AtomicU8,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
mqtt_client: AsyncClient,
|
||||
topics: Vec<String>,
|
||||
lights_state: Lights,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LightsData {
|
||||
on: bool,
|
||||
brightness: Option<u8>,
|
||||
}
|
||||
|
||||
const INDEX_HTML: &'static str = r#"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Licht 24 - Licht hier jetzt!</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="">
|
||||
<script>
|
||||
function post_brightness() {
|
||||
fetch("/lights", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
on: true,
|
||||
brightness: parseInt(document.getElementById("brightness").value)
|
||||
}),
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8"
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<h3>Helligkeit</h3>
|
||||
<input type="range" id="brightness" name="brightness" min="1" max="255" onmouseup="post_brightness()" ontouchend="post_brightness()" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>"#;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut mqttoptions = MqttOptions::new("licht24", "192.168.3.145", 1883);
|
||||
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
||||
|
||||
let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10);
|
||||
|
||||
let shared_state = Arc::new(AppState {
|
||||
mqtt_client: client,
|
||||
topics: vec![
|
||||
String::from("zigbee2mqtt/sofaecke/decke_1/set"),
|
||||
String::from("zigbee2mqtt/sofaecke/decke_2/set"),
|
||||
String::from("zigbee2mqtt/sofaecke/decke_3/set"),
|
||||
String::from("zigbee2mqtt/sitzecke/decke_1/set"),
|
||||
String::from("zigbee2mqtt/sitzecke/decke_2/set"),
|
||||
String::from("zigbee2mqtt/trinkregal/n_1/set"),
|
||||
String::from("zigbee2mqtt/trinkregal/n_2/set"),
|
||||
String::from("zigbee2mqtt/elektro_ecke/decke_1/set"),
|
||||
String::from("zigbee2mqtt/elektro_ecke/decke_2/set"),
|
||||
String::from("zigbee2mqtt/spind/set"),
|
||||
String::from("zigbee2mqtt/3ddruck/wand/set"),
|
||||
],
|
||||
lights_state: Lights {
|
||||
on: AtomicBool::new(true),
|
||||
brightness: AtomicU8::new(100),
|
||||
},
|
||||
});
|
||||
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
let notification = eventloop
|
||||
.poll()
|
||||
.await
|
||||
.expect("Couldn't poll MQTT eventloop.");
|
||||
println!("Received = {:?}", notification);
|
||||
}
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(|| async { Html(INDEX_HTML) }))
|
||||
.route("/lights", post(set_lights))
|
||||
.with_state(shared_state);
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
||||
.await
|
||||
.expect("Unable to bind to port 3000.");
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
async fn set_lights(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(body): Json<LightsData>,
|
||||
) -> Json<LightsData> {
|
||||
if let Some(brightness) = body.brightness {
|
||||
for topic in &state.topics {
|
||||
match state
|
||||
.mqtt_client
|
||||
.publish(
|
||||
topic,
|
||||
QoS::AtMostOnce,
|
||||
false,
|
||||
format!("{{\"state\": \"ON\", \"brightness\": {0}}}", brightness),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
state
|
||||
.lights_state
|
||||
.brightness
|
||||
.store(brightness, Ordering::Relaxed);
|
||||
state.lights_state.on.store(true, Ordering::Relaxed);
|
||||
info!(
|
||||
"Successfully published brightness level: {0} to topic: {topic}",
|
||||
brightness
|
||||
)
|
||||
}
|
||||
Err(err) => warn!("Unable to publish to topic: {topic} with error: {err}"),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for topic in &state.topics {
|
||||
match state
|
||||
.mqtt_client
|
||||
.publish(
|
||||
topic,
|
||||
QoS::AtMostOnce,
|
||||
false,
|
||||
match body.on {
|
||||
true => format!("{{\"state\": \"ON\"}}"),
|
||||
false => format!("{{\"state\": \"OFF\"}}"),
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
state.lights_state.on.store(body.on, Ordering::Relaxed);
|
||||
info!(
|
||||
"Successfully published brightness state: {0} to topic: {topic}",
|
||||
match body.on {
|
||||
true => "ON",
|
||||
false => "OFF",
|
||||
},
|
||||
)
|
||||
}
|
||||
Err(err) => warn!("Unable to publish to topic: {topic} with error: {err}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Json(body)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue