Instead of being a function it now expects a struct with the PartialUserData trait implemented. This in part ensures the correct function signature. It also adds another optional function to PartialUserData that returns definitions for the added methods.
126 lines
3.2 KiB
Rust
126 lines
3.2 KiB
Rust
use std::convert::Infallible;
|
|
use std::net::SocketAddr;
|
|
|
|
use async_trait::async_trait;
|
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
|
use automation_lib::lua::traits::PartialUserData;
|
|
use automation_macro::{Device, LuaDeviceConfig};
|
|
use lua_typed::Typed;
|
|
use mlua::LuaSerdeExt;
|
|
use serde::{Deserialize, Serialize};
|
|
use tracing::{error, trace, warn};
|
|
|
|
#[derive(Debug, Deserialize, Typed)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[typed(rename_all = "snake_case")]
|
|
pub enum Flag {
|
|
Presence,
|
|
Darkness,
|
|
}
|
|
crate::register_type!(Flag);
|
|
|
|
#[derive(Debug, Clone, Deserialize, Typed)]
|
|
pub struct FlagIDs {
|
|
presence: isize,
|
|
darkness: isize,
|
|
}
|
|
crate::register_type!(FlagIDs);
|
|
|
|
#[derive(Debug, LuaDeviceConfig, Clone, Typed)]
|
|
#[typed(as = "HueBridgeConfig")]
|
|
pub struct Config {
|
|
pub identifier: String,
|
|
#[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 80)))]
|
|
#[typed(as = "ip")]
|
|
pub addr: SocketAddr,
|
|
pub login: String,
|
|
pub flags: FlagIDs,
|
|
}
|
|
crate::register_type!(Config);
|
|
|
|
#[derive(Debug, Clone, Device)]
|
|
#[device(extra_user_data = SetFlag)]
|
|
pub struct HueBridge {
|
|
config: Config,
|
|
}
|
|
crate::register_device!(HueBridge);
|
|
|
|
struct SetFlag;
|
|
impl PartialUserData<HueBridge> for SetFlag {
|
|
fn add_methods<M: mlua::UserDataMethods<HueBridge>>(methods: &mut M) {
|
|
methods.add_async_method(
|
|
"set_flag",
|
|
async |lua, this, (flag, value): (mlua::Value, bool)| {
|
|
let flag: Flag = lua.from_value(flag)?;
|
|
|
|
this.set_flag(flag, value).await;
|
|
|
|
Ok(())
|
|
},
|
|
);
|
|
}
|
|
|
|
fn definitions() -> Option<String> {
|
|
Some(format!(
|
|
"---@async\n---@param flag {}\n---@param value boolean\nfunction {}:set_flag(flag, value) end\n",
|
|
<Flag as Typed>::type_name(),
|
|
<HueBridge as Typed>::type_name(),
|
|
))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct FlagMessage {
|
|
flag: bool,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl LuaDeviceCreate for HueBridge {
|
|
type Config = Config;
|
|
type Error = Infallible;
|
|
|
|
async fn create(config: Self::Config) -> Result<Self, Infallible> {
|
|
trace!(id = config.identifier, "Setting up HueBridge");
|
|
Ok(Self { config })
|
|
}
|
|
}
|
|
|
|
impl HueBridge {
|
|
pub async fn set_flag(&self, flag: Flag, value: bool) {
|
|
let flag_id = match flag {
|
|
Flag::Presence => self.config.flags.presence,
|
|
Flag::Darkness => self.config.flags.darkness,
|
|
};
|
|
|
|
let url = format!(
|
|
"http://{}/api/{}/sensors/{flag_id}/state",
|
|
self.config.addr, self.config.login
|
|
);
|
|
|
|
trace!(?flag, flag_id, value, "Sending request to change flag");
|
|
let res = reqwest::Client::new()
|
|
.put(url)
|
|
.json(&FlagMessage { flag: value })
|
|
.send()
|
|
.await;
|
|
|
|
match res {
|
|
Ok(res) => {
|
|
let status = res.status();
|
|
if !status.is_success() {
|
|
warn!(flag_id, "Status code is not success: {status}");
|
|
}
|
|
}
|
|
Err(err) => {
|
|
error!(flag_id, "Error: {err}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Device for HueBridge {
|
|
fn get_id(&self) -> String {
|
|
self.config.identifier.clone()
|
|
}
|
|
}
|