Add base code
This commit is contained in:
commit
0403048e7b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1713
Cargo.lock
generated
Normal file
1713
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "ir-remote"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = {version = "1", features = ["full"]}
|
||||||
|
serialport = "4"
|
||||||
|
env_logger = "0.9"
|
||||||
|
log = "0.4"
|
||||||
|
serde = {version = "1", features = ["derive"]}
|
||||||
|
rocket = "0.5.0-rc.2"
|
||||||
|
serde_json = "1"
|
||||||
|
futures = "0.3"
|
||||||
|
strum_macros = "0.24"
|
||||||
|
strum = { version = "0.24", features = ["derive"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
ructe = "0.14"
|
5
README.adoc
Normal file
5
README.adoc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
= ir-remote
|
||||||
|
|
||||||
|
A personal project for remote controlling IR devices
|
||||||
|
|
||||||
|
link:https://raw.githubusercontent.com/probonopd/irdb/master/codes/Samsung/TV/7%2C7.csv[Samsung TV Codes]
|
3
Rocket.toml
Normal file
3
Rocket.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[default.shutdown]
|
||||||
|
ctrlc = false
|
||||||
|
signals = ["term", "hup"]
|
23
arduino/Makefile
Normal file
23
arduino/Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
BOARD = arduino:avr:uno
|
||||||
|
PORT = /dev/ttyACM0
|
||||||
|
SERIAL = 9600
|
||||||
|
SCREEN_NAME = arduino
|
||||||
|
DEPS = IRremote
|
||||||
|
|
||||||
|
.PHONY: compile
|
||||||
|
compile:
|
||||||
|
arduino-cli compile -v --libraries ./libraries/ --fqbn arduino:avr:uno .
|
||||||
|
|
||||||
|
deploy: compile
|
||||||
|
# Screen can't be consuming the TTY
|
||||||
|
if screen -S $(SCREEN_NAME) -Q select .; then screen -XS $(SCREEN_NAME) quit; fi
|
||||||
|
|
||||||
|
# Upload the code now that screen isn't running
|
||||||
|
arduino-cli upload --fqbn $(BOARD) --port $(PORT) .
|
||||||
|
|
||||||
|
deps:
|
||||||
|
arduino-cli lib install $(DEPS)
|
||||||
|
|
||||||
|
screen:
|
||||||
|
# -dR - Reattach; logout remotely if required
|
||||||
|
screen -DRqS $(SCREEN_NAME) $(PORT) $(SERIAL)
|
93
arduino/arduino.ino
Normal file
93
arduino/arduino.ino
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <IRremote.hpp>
|
||||||
|
#include <Arduino.h>
|
||||||
|
// define IR_SEND_PIN 4
|
||||||
|
|
||||||
|
int IR_RECEIVE_PIN = 11;
|
||||||
|
int IR_SEND_PIN = 4;
|
||||||
|
|
||||||
|
String delim = ",";
|
||||||
|
|
||||||
|
uint16_t sAddress = 0x0000;
|
||||||
|
uint8_t sCommand = 0x00;
|
||||||
|
uint8_t sRepeats = 0x00;
|
||||||
|
bool readyToSend = false;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
while (!Serial) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receiver
|
||||||
|
Serial.print("Setting up receiver pin ");
|
||||||
|
Serial.println(IR_RECEIVE_PIN);
|
||||||
|
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
|
||||||
|
|
||||||
|
// Sender
|
||||||
|
Serial.print("Setting up sender pin ");
|
||||||
|
Serial.println(IR_SEND_PIN);
|
||||||
|
pinMode(IR_SEND_PIN, OUTPUT);
|
||||||
|
IrSender.begin(IR_SEND_PIN);
|
||||||
|
|
||||||
|
Serial.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (IrReceiver.decode()) {
|
||||||
|
IrReceiver.printIRResultShort(&Serial);
|
||||||
|
IrReceiver.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (readyToSend) {
|
||||||
|
// Send out an actual result
|
||||||
|
sendData();
|
||||||
|
// TODO: Don't need to have this variable in the future probably
|
||||||
|
readyToSend = false;
|
||||||
|
} else {
|
||||||
|
// If we aren't sending data, then check for serial data
|
||||||
|
|
||||||
|
// Try to parse serial, and if you can, set readyToSend to true
|
||||||
|
readyToSend = parseSerial();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (parseSerial()) {
|
||||||
|
sendData();
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(1000); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseSerial() {
|
||||||
|
if (Serial.available() == 0) {
|
||||||
|
// There is no data for us
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String input = Serial.readStringUntil('\n');
|
||||||
|
|
||||||
|
Serial.print("Got serial message: ");
|
||||||
|
Serial.println(input);
|
||||||
|
Serial.flush();
|
||||||
|
|
||||||
|
sAddress = atoi(strtok(input.c_str(), delim.c_str()));
|
||||||
|
sCommand = atoi(strtok(NULL, delim.c_str()));
|
||||||
|
sRepeats = atoi(strtok(NULL, delim.c_str()));
|
||||||
|
|
||||||
|
// If we have a real address and command, we successfully parsed
|
||||||
|
return sAddress && sCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendData() {
|
||||||
|
Serial.print("Send now: protocol: Samsung address=0x");
|
||||||
|
Serial.print(sAddress, HEX);
|
||||||
|
Serial.print(" command=0x");
|
||||||
|
Serial.print(sCommand, HEX);
|
||||||
|
Serial.print(" repeats=");
|
||||||
|
Serial.println(sRepeats);
|
||||||
|
Serial.flush();
|
||||||
|
|
||||||
|
IrSender.sendSamsung(sAddress, sCommand, sRepeats);
|
||||||
|
}
|
9
build.rs
Normal file
9
build.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use ructe::Ructe;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut ructe = Ructe::from_env()?;
|
||||||
|
ructe.statics()?.add_files("static")?;
|
||||||
|
ructe.compile_templates("templates")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
139
src/ir_types.rs
Normal file
139
src/ir_types.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use rocket::request::FromRequest;
|
||||||
|
use rocket::FromFormField;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use strum_macros::AsRefStr;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub enum DeviceType {
|
||||||
|
Samsung,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceType {
|
||||||
|
pub fn device_code(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::Samsung => 0x707,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_code(&self, function: &Function) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Samsung => match function {
|
||||||
|
Function::InputSource => 0x1,
|
||||||
|
Function::Power => 0x2,
|
||||||
|
Function::Button1 => 0x4,
|
||||||
|
Function::Button2 => 0x5,
|
||||||
|
Function::Button3 => 0x6,
|
||||||
|
Function::VolumeUp => 0x7,
|
||||||
|
Function::Button4 => 0x8,
|
||||||
|
Function::Button5 => 0x9,
|
||||||
|
Function::Button6 => 0xa,
|
||||||
|
Function::VolumeDown => 0xb,
|
||||||
|
Function::Button7 => 0xc,
|
||||||
|
Function::Button8 => 0xd,
|
||||||
|
Function::Button9 => 0xe,
|
||||||
|
Function::Mute => 0xf,
|
||||||
|
Function::ChannelDown => 0x10,
|
||||||
|
Function::Button0 => 0x11,
|
||||||
|
Function::ChannelUp => 0x12,
|
||||||
|
Function::Last => 0x13,
|
||||||
|
Function::Menu => 0x1a,
|
||||||
|
Function::Info => 0x1f,
|
||||||
|
Function::AddSubtract => 0x25,
|
||||||
|
Function::Exit => 0x2d,
|
||||||
|
Function::EManual => 0x3f,
|
||||||
|
Function::Tools => 0x4b,
|
||||||
|
Function::Guide => 0x4f,
|
||||||
|
Function::Return => 0x58,
|
||||||
|
Function::CursorUp => 0x60,
|
||||||
|
Function::CursorDown => 0x61,
|
||||||
|
Function::CursorRight => 0x62,
|
||||||
|
Function::CursorLeft => 0x65,
|
||||||
|
Function::Enter => 0x68,
|
||||||
|
Function::ChannelList => 0x6b,
|
||||||
|
Function::SmartHub => 0x79,
|
||||||
|
Function::Button3D => 0x9f,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, EnumIter, AsRefStr, FromFormField, Debug, Copy, Clone)]
|
||||||
|
pub enum Function {
|
||||||
|
InputSource,
|
||||||
|
Power,
|
||||||
|
Button1,
|
||||||
|
Button2,
|
||||||
|
Button3,
|
||||||
|
VolumeUp,
|
||||||
|
Button4,
|
||||||
|
Button5,
|
||||||
|
Button6,
|
||||||
|
VolumeDown,
|
||||||
|
Button7,
|
||||||
|
Button8,
|
||||||
|
Button9,
|
||||||
|
Mute,
|
||||||
|
ChannelDown,
|
||||||
|
Button0,
|
||||||
|
ChannelUp,
|
||||||
|
Last,
|
||||||
|
Menu,
|
||||||
|
Info,
|
||||||
|
AddSubtract,
|
||||||
|
Exit,
|
||||||
|
EManual,
|
||||||
|
Tools,
|
||||||
|
Guide,
|
||||||
|
Return,
|
||||||
|
CursorUp,
|
||||||
|
CursorDown,
|
||||||
|
CursorRight,
|
||||||
|
CursorLeft,
|
||||||
|
Enter,
|
||||||
|
ChannelList,
|
||||||
|
SmartHub,
|
||||||
|
Button3D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
pub fn as_ref_pretty(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::InputSource => "Input Source",
|
||||||
|
Self::Power => "Power",
|
||||||
|
Self::Button1 => "Button 1",
|
||||||
|
Self::Button2 => "Button 2",
|
||||||
|
Self::Button3 => "Button 3",
|
||||||
|
Self::VolumeUp => "Volume Up",
|
||||||
|
Self::Button4 => "Button 4",
|
||||||
|
Self::Button5 => "Button 5",
|
||||||
|
Self::Button6 => "Button 6",
|
||||||
|
Self::VolumeDown => "Volume Down",
|
||||||
|
Self::Button7 => "Button 7",
|
||||||
|
Self::Button8 => "Button 8",
|
||||||
|
Self::Button9 => "Button 9",
|
||||||
|
Self::Mute => "Mute",
|
||||||
|
Self::ChannelDown => "Channel Down",
|
||||||
|
Self::Button0 => "Button 0",
|
||||||
|
Self::ChannelUp => "Channel Up",
|
||||||
|
Self::Last => "Last",
|
||||||
|
Self::Menu => "Menu",
|
||||||
|
Self::Info => "Info",
|
||||||
|
Self::AddSubtract => "Add Subtract",
|
||||||
|
Self::Exit => "Exit",
|
||||||
|
Self::EManual => "E-Manual",
|
||||||
|
Self::Tools => "Tools",
|
||||||
|
Self::Guide => "Guide",
|
||||||
|
Self::Return => "Return",
|
||||||
|
Self::CursorUp => "Cursor Up",
|
||||||
|
Self::CursorDown => "Cursor Down",
|
||||||
|
Self::CursorRight => "Cursor Right",
|
||||||
|
Self::CursorLeft => "Cursor Left",
|
||||||
|
Self::Enter => "Enter",
|
||||||
|
Self::ChannelList => "Channel List",
|
||||||
|
Self::SmartHub => "Smart Hub",
|
||||||
|
Self::Button3D => "Button 3D",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
src/main.rs
Normal file
101
src/main.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#![allow(unused_imports)]
|
||||||
|
mod ir_types;
|
||||||
|
mod serial_agent;
|
||||||
|
mod webui;
|
||||||
|
use env_logger::Env;
|
||||||
|
use futures::future::join_all;
|
||||||
|
use ir_types::DeviceType;
|
||||||
|
use ir_types::Function;
|
||||||
|
use log::{error, info};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::sync::mpsc::channel;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
use tokio::time;
|
||||||
|
|
||||||
|
type AppResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
|
||||||
|
struct Options {
|
||||||
|
// device: DeviceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> AppResult<()> {
|
||||||
|
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||||
|
info!("Starting ir remote");
|
||||||
|
|
||||||
|
let options = Arc::new(Options {
|
||||||
|
// device: DeviceType::Samsung,
|
||||||
|
});
|
||||||
|
|
||||||
|
let (outbound_serial_tx, outbound_serial_rx) = channel(100);
|
||||||
|
let (inbound_serial_tx, inbound_serial_rx) = channel(100);
|
||||||
|
|
||||||
|
info!("Starting serial port agent");
|
||||||
|
let agent_handle: JoinHandle<AppResult<()>> = tokio::task::spawn(async move {
|
||||||
|
serial_agent::serialport_agent(inbound_serial_tx, outbound_serial_rx).await
|
||||||
|
});
|
||||||
|
|
||||||
|
// let (agent_shutdown_tx, agent_handle): (Sender<()>, JoinHandle<AppResult<()>>) = {
|
||||||
|
// let (shutdown_tx, shutdown_rx) = channel(1);
|
||||||
|
// (
|
||||||
|
// shutdown_tx,
|
||||||
|
// tokio::task::spawn(async move {
|
||||||
|
// serial_agent::serialport_agent(shutdown_rx, inbound_serial_tx, outbound_serial_rx)
|
||||||
|
// .await
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
// };
|
||||||
|
|
||||||
|
// info!("Starting debugging");
|
||||||
|
// let debugging_handle: JoinHandle<AppResult<()>> = {
|
||||||
|
// let outbound_serial_tx = outbound_serial_tx.clone();
|
||||||
|
|
||||||
|
// tokio::task::spawn(async move {
|
||||||
|
// info!("In debugging handle");
|
||||||
|
// time::sleep(Duration::from_secs(10)).await;
|
||||||
|
// info!("Done waiting");
|
||||||
|
|
||||||
|
// let msg = format!(
|
||||||
|
// "{},{},{}\n",
|
||||||
|
// options.device.device_code(),
|
||||||
|
// options.device.function_code(&Function::Power),
|
||||||
|
// 3
|
||||||
|
// );
|
||||||
|
|
||||||
|
// outbound_serial_tx.send(msg).await?;
|
||||||
|
|
||||||
|
// info!("Done with debugging handle");
|
||||||
|
// Ok(())
|
||||||
|
// })
|
||||||
|
// };
|
||||||
|
|
||||||
|
info!("Starting webui");
|
||||||
|
let webui_handle: rocket::Shutdown = webui::launch(webui::Settings {
|
||||||
|
outbound_serial_tx: outbound_serial_tx.clone(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
tokio::select!(
|
||||||
|
r = agent_handle => {
|
||||||
|
error!("Agent handle ended: {r:?}");
|
||||||
|
}
|
||||||
|
r = webui_handle => {
|
||||||
|
// agent_shutdown_tx.send(()).await?;
|
||||||
|
error!("Webserver handle ended: {r:?}");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// tokio::join!(
|
||||||
|
// tokio::spawn(async move {
|
||||||
|
// error!("Agent handle ended: {:?}", agent_handle.await);
|
||||||
|
// }),
|
||||||
|
// tokio::spawn(async move {
|
||||||
|
// let _ = agent_shutdown_tx.send(()).await;
|
||||||
|
// error!("Webui handle ended: {:?}", webui_handle.await);
|
||||||
|
// }),
|
||||||
|
// );
|
||||||
|
|
||||||
|
panic!("Ending program");
|
||||||
|
}
|
83
src/serial_agent.rs
Normal file
83
src/serial_agent.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
use crate::AppResult;
|
||||||
|
use log::error;
|
||||||
|
use log::info;
|
||||||
|
use serialport::SerialPort;
|
||||||
|
use serialport::TTYPort;
|
||||||
|
use std::io::BufRead;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
pub async fn serialport_agent(
|
||||||
|
// mut shutdown_rx: Receiver<()>,
|
||||||
|
inbound_serial_tx: Sender<String>,
|
||||||
|
outbound_serial_rx: Receiver<String>,
|
||||||
|
) -> AppResult<()> {
|
||||||
|
info!("Opening serial port");
|
||||||
|
|
||||||
|
let (outbound_port, inbound_port): (TTYPort, TTYPort) =
|
||||||
|
spawn_blocking(move || -> AppResult<(TTYPort, TTYPort)> {
|
||||||
|
let port = serialport::new("/dev/ttyACM0", 9600)
|
||||||
|
.timeout(Duration::from_millis(1000))
|
||||||
|
.open_native()?;
|
||||||
|
let port_clone = port.try_clone_native()?;
|
||||||
|
|
||||||
|
Ok((port, port_clone))
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
tokio::select!(
|
||||||
|
// _ = shutdown_rx.recv() => {
|
||||||
|
// return Ok(());
|
||||||
|
// }
|
||||||
|
r = spawn_blocking(move || outbound(Box::new(outbound_port), outbound_serial_rx)) => {
|
||||||
|
error!("Outbound thread crashed: {r:?}");
|
||||||
|
}
|
||||||
|
r = spawn_blocking(move || inbound(Box::new(inbound_port), inbound_serial_tx)) => {
|
||||||
|
error!("Inbound thread crashed: {r:?}");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
error!("Some agent channel closed");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbound(port: Box<dyn SerialPort>, inbound_serial_tx: Sender<String>) -> AppResult<()> {
|
||||||
|
let reader = BufReader::new(port);
|
||||||
|
|
||||||
|
for msg in reader.lines() {
|
||||||
|
match msg {
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
|
||||||
|
// Timed out the message. Not a big deal
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not read line: {e:?}");
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
Ok(m) => {
|
||||||
|
info!("Got message from inbound channel: {m}");
|
||||||
|
inbound_serial_tx.blocking_send(m)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Return error since something crashed
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outbound(
|
||||||
|
mut port: Box<dyn SerialPort>,
|
||||||
|
mut outbound_serial_rx: Receiver<String>,
|
||||||
|
) -> AppResult<()> {
|
||||||
|
while let Some(msg) = outbound_serial_rx.blocking_recv() {
|
||||||
|
info!("Sending message {msg}");
|
||||||
|
port.write_all(msg.as_bytes())?;
|
||||||
|
port.flush()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("The channel was closed");
|
||||||
|
Ok(())
|
||||||
|
}
|
135
src/webui.rs
Normal file
135
src/webui.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||||
|
mod template_responder;
|
||||||
|
use crate::ir_types::DeviceType;
|
||||||
|
use crate::ir_types::Function;
|
||||||
|
use crate::AppResult;
|
||||||
|
use log::error;
|
||||||
|
use log::info;
|
||||||
|
use log::warn;
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::get;
|
||||||
|
use rocket::launch;
|
||||||
|
use rocket::post;
|
||||||
|
use rocket::response::Flash;
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
use rocket::routes;
|
||||||
|
use rocket::uri;
|
||||||
|
use rocket::FromForm;
|
||||||
|
use rocket::State;
|
||||||
|
use template_responder::TemplateResponder;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
pub struct Settings {
|
||||||
|
pub outbound_serial_tx: Sender<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WebuiState {
|
||||||
|
outbound_serial_tx: Sender<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static files
|
||||||
|
// use templates::statics::StaticFile;
|
||||||
|
|
||||||
|
#[derive(FromForm, Debug)]
|
||||||
|
struct FunctionForm {
|
||||||
|
function: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/function?<function>")]
|
||||||
|
async fn function_get(function: Function, state: &State<WebuiState>) -> Flash<Redirect> {
|
||||||
|
info!("Got button value: {:?}", function);
|
||||||
|
|
||||||
|
match state
|
||||||
|
.outbound_serial_tx
|
||||||
|
.send(format!(
|
||||||
|
"{},{},{}\n",
|
||||||
|
DeviceType::Samsung.device_code(),
|
||||||
|
DeviceType::Samsung.function_code(&function),
|
||||||
|
1
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e) => {
|
||||||
|
let error_msg = format!("{e:?}");
|
||||||
|
error!("{error_msg}");
|
||||||
|
Flash::error(Redirect::to(uri!(index)), error_msg)
|
||||||
|
}
|
||||||
|
Ok(()) => Flash::success(Redirect::to(uri!(index)), "Successfully changed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/function", data = "<data>")]
|
||||||
|
async fn function_post(data: Form<FunctionForm>, state: &State<WebuiState>) -> Flash<Redirect> {
|
||||||
|
function_get(data.function, state).await
|
||||||
|
// // info!("Got button value: {:?}", data.function);
|
||||||
|
|
||||||
|
// // match state
|
||||||
|
// // .outbound_serial_tx
|
||||||
|
// // .send(format!(
|
||||||
|
// // "{},{},{}\n",
|
||||||
|
// // DeviceType::Samsung.device_code(),
|
||||||
|
// // DeviceType::Samsung.function_code(&Function::Power),
|
||||||
|
// // 1
|
||||||
|
// // ))
|
||||||
|
// // .await
|
||||||
|
// // {
|
||||||
|
// // Err(e) => {
|
||||||
|
// // let error_msg = format!("{e:?}");
|
||||||
|
// // error!("{error_msg}");
|
||||||
|
// // Flash::error(Redirect::to(uri!(index)), error_msg)
|
||||||
|
// // }
|
||||||
|
// // Ok(()) => Flash::success(Redirect::to(uri!(index)), "Successfully changed"),
|
||||||
|
// // }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/power")]
|
||||||
|
async fn power(state: &State<WebuiState>) -> Flash<Redirect> {
|
||||||
|
match state
|
||||||
|
.outbound_serial_tx
|
||||||
|
.send(format!(
|
||||||
|
"{},{},{}\n",
|
||||||
|
DeviceType::Samsung.device_code(),
|
||||||
|
DeviceType::Samsung.function_code(&Function::Power),
|
||||||
|
1
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(e) => {
|
||||||
|
let error_msg = format!("{e:?}");
|
||||||
|
error!("{error_msg}");
|
||||||
|
Flash::error(Redirect::to(uri!(index)), error_msg)
|
||||||
|
}
|
||||||
|
Ok(()) => Flash::success(Redirect::to(uri!(index)), "Successfully changed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index() -> Result<TemplateResponder, std::io::Error> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
templates::index_html(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(TemplateResponder::from(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn launch(settings: Settings) -> AppResult<rocket::Shutdown> {
|
||||||
|
let state = WebuiState {
|
||||||
|
outbound_serial_tx: settings.outbound_serial_tx,
|
||||||
|
};
|
||||||
|
let rocket = rocket::build()
|
||||||
|
// let _rocket = rocket::build()
|
||||||
|
// .mount("/", routes![index, function, power])
|
||||||
|
// .manage(state)
|
||||||
|
// .launch()
|
||||||
|
// .await?;
|
||||||
|
.mount("/", routes![index, function_get, function_post, power])
|
||||||
|
.manage(state)
|
||||||
|
.ignite()
|
||||||
|
.await?;
|
||||||
|
let shutdown_handle = rocket.shutdown();
|
||||||
|
|
||||||
|
tokio::task::spawn(rocket.launch());
|
||||||
|
|
||||||
|
Ok(shutdown_handle)
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
}
|
20
src/webui/template_responder.rs
Normal file
20
src/webui/template_responder.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use rocket::{http::ContentType, response::Responder, Request, Response};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
pub struct TemplateResponder(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for TemplateResponder {
|
||||||
|
fn from(d: Vec<u8>) -> Self {
|
||||||
|
Self(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> Responder<'r, 'static> for TemplateResponder {
|
||||||
|
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'static> {
|
||||||
|
Response::build()
|
||||||
|
.header(ContentType::HTML)
|
||||||
|
.sized_body(self.0.len(), Cursor::new(self.0))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
1249
static/remote.svg
Normal file
1249
static/remote.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 83 KiB |
1055
static/remote2.svg
Normal file
1055
static/remote2.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 83 KiB |
2
static/tacit-css.min.css
vendored
Normal file
2
static/tacit-css.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1086
templates/index.rs.html
Normal file
1086
templates/index.rs.html
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user