Dirty WIP
This commit is contained in:
parent
2df8a9351f
commit
d41d5bd3cd
2031
Cargo.lock
generated
2031
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ libloading = "0.5.0"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_xml = "0.9"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
blurz = "0.4.0"
|
blurz = "0.4.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
@ -18,4 +19,5 @@ log = "0.4"
|
|||||||
simplelog = "0.5"
|
simplelog = "0.5"
|
||||||
reqwest = "0.9"
|
reqwest = "0.9"
|
||||||
percent-encoding = "1.0"
|
percent-encoding = "1.0"
|
||||||
rand = "0.6"
|
rand = "0.6"
|
||||||
|
trust-dns-resolver = "0.19"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![feature(unrestricted_attribute_tokens)]
|
#![feature(register_tool)]
|
||||||
#![feature(custom_attribute)]
|
#![feature(register_attr)]
|
||||||
#![feature(try_from)]
|
|
||||||
|
|
||||||
mod filter;
|
mod filter;
|
||||||
mod message;
|
mod message;
|
||||||
|
83
src/monitors/dns.rs
Normal file
83
src/monitors/dns.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
pub use crate::message::{Level, Message};
|
||||||
|
pub use crate::monitor::*;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
// use std::net::*;
|
||||||
|
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
|
||||||
|
use trust_dns_resolver::Resolver;
|
||||||
|
|
||||||
|
// #[derive(Debug)]
|
||||||
|
pub struct DNS {
|
||||||
|
config: DNSConfig,
|
||||||
|
resolver: Resolver,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct DNSConfig {
|
||||||
|
#[serde(default)]
|
||||||
|
records: Vec<DNSConfigRecord>,
|
||||||
|
#[serde(default)]
|
||||||
|
period: Option<f64>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct DNSConfigRecord {
|
||||||
|
domain_name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
resolver: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
entries: Vec<DNSConfigEntry>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct DNSConfigEntry {
|
||||||
|
record_type: String,
|
||||||
|
#[serde(default)]
|
||||||
|
record_value: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
resolver: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Monitor for DNS {
|
||||||
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let config: DNSConfig = serde_yaml::from_value(config)?;
|
||||||
|
|
||||||
|
let mut resolver =
|
||||||
|
Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap();
|
||||||
|
|
||||||
|
Ok(DNS {
|
||||||
|
config: config,
|
||||||
|
resolver: resolver,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||||
|
for record in &self.config.records {
|
||||||
|
//let mut response = match &record.record_type {
|
||||||
|
// "ipv4" => self.resolver.ipv4_lookup(&record.domain_name),
|
||||||
|
// // "ipv6" => self.resolver.ipv6_lookup(&record.domain_name),
|
||||||
|
// _ => self.resolver.ipv4_lookup(&record.domain_name),
|
||||||
|
//};
|
||||||
|
|
||||||
|
// let mut response = self.resolver.lookup_ip(&record.domain_name);
|
||||||
|
// if let Err(e) = response {
|
||||||
|
// sender
|
||||||
|
// .send(Message {
|
||||||
|
// emitter: "dns".to_owned(),
|
||||||
|
// msg_type: "dns.resolve".to_owned(),
|
||||||
|
// level: Level::Issue,
|
||||||
|
// text: format!("Cannot resolve hostname {}", &record.domain_name),
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// let Ok(response) = response;
|
||||||
|
// while let Some(res) = response.next() {
|
||||||
|
// println!("res={:?}", res);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
let delay_ms = match self.config.period {
|
||||||
|
Some(sec) => (sec * 1000.0) as u64,
|
||||||
|
None => 2000,
|
||||||
|
};
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(delay_ms));
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,7 @@ struct DummySenderConfig {
|
|||||||
|
|
||||||
impl Monitor for DummySender {
|
impl Monitor for DummySender {
|
||||||
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let config: DummySenderConfig =
|
let config: DummySenderConfig = serde_yaml::from_value(config)?;
|
||||||
serde_yaml::from_value(config).expect("Invalid config for dummy_sender");
|
|
||||||
|
|
||||||
Ok(DummySender {
|
Ok(DummySender {
|
||||||
cnt: 0,
|
cnt: 0,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
pub mod dhcp_leases;
|
pub mod dhcp_leases;
|
||||||
|
pub mod dns;
|
||||||
pub mod dummy_sender;
|
pub mod dummy_sender;
|
||||||
|
pub mod nmap_scanner;
|
||||||
|
pub mod ping;
|
||||||
pub mod wifi_availability;
|
pub mod wifi_availability;
|
||||||
|
|
||||||
use crate::monitor::*;
|
use crate::monitor::*;
|
||||||
@ -12,6 +15,9 @@ pub fn factory(
|
|||||||
"dummy_sender" => Ok(Box::new(dummy_sender::DummySender::new(config)?)),
|
"dummy_sender" => Ok(Box::new(dummy_sender::DummySender::new(config)?)),
|
||||||
"wifi_availability" => Ok(Box::new(wifi_availability::WifiAvailability::new(config)?)),
|
"wifi_availability" => Ok(Box::new(wifi_availability::WifiAvailability::new(config)?)),
|
||||||
"dhcp_leases" => Ok(Box::new(dhcp_leases::DHCPLeases::new(config)?)),
|
"dhcp_leases" => Ok(Box::new(dhcp_leases::DHCPLeases::new(config)?)),
|
||||||
|
"nmap_scanner" => Ok(Box::new(nmap_scanner::NmapScanner::new(config)?)),
|
||||||
|
"ping" => Ok(Box::new(ping::Ping::new(config)?)),
|
||||||
|
"dns" => Ok(Box::new(dns::DNS::new(config)?)),
|
||||||
_ => panic!("Unknown monitor name: {}", name),
|
_ => panic!("Unknown monitor name: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,13 @@ pub use crate::monitor::*;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
use regex::Regex;
|
// use regex::Regex;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
use chrono::Local;
|
// use chrono::Local;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NmapScanner {
|
pub struct NmapScanner {
|
||||||
config: NmapScannerConfig,
|
config: NmapScannerConfig,
|
||||||
rgx_lease: Regex,
|
|
||||||
rgx_mac: Regex,
|
|
||||||
rgx_date_start: Regex,
|
|
||||||
rgx_date_ends: Regex,
|
|
||||||
}
|
}
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
struct NmapScannerConfig {
|
struct NmapScannerConfig {
|
||||||
@ -21,144 +17,62 @@ struct NmapScannerConfig {
|
|||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
ip_range: Option<String>,
|
ip_range: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
period: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Monitor for NmapScanner {
|
impl Monitor for NmapScanner {
|
||||||
fn new(
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
config: serde_yaml::Value,
|
let mut config: NmapScannerConfig = serde_yaml::from_value(config)?;
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
|
||||||
let mut config = serde_yaml::from_value(config)?;
|
|
||||||
|
|
||||||
if config.ip_range.is_none() {
|
if config.ip_range.is_none() {
|
||||||
// Detect current subnet
|
// Detect current subnet
|
||||||
|
|
||||||
config.ip_range = Some("192.168.0.1-254");
|
config.ip_range = Some("192.168.0.1-254".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex compilation
|
// Regex compilation
|
||||||
let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
|
// let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap();
|
||||||
let rgx_mac =
|
// let rgx_mac =
|
||||||
Regex::new(r"(?m)^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;")
|
// Regex::new(r"(?m)^\s*hardware\s+ethernet\s([a-f0-9]{2}(?::[a-f0-9]{2}){5})\s*;")
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
let rgx_date_start = Regex::new(r"(?m)^\s*starts\s+\d+\s+(.*?)\s*;").unwrap();
|
// let rgx_date_start = Regex::new(r"(?m)^\s*starts\s+\d+\s+(.*?)\s*;").unwrap();
|
||||||
let rgx_date_ends = Regex::new(r"(?m)^\s*ends\s+\d+\s+(.*?)\s*;").unwrap();
|
// let rgx_date_ends = Regex::new(r"(?m)^\s*ends\s+\d+\s+(.*?)\s*;").unwrap();
|
||||||
|
|
||||||
Ok(NmapScanner {
|
Ok(NmapScanner { config: config })
|
||||||
config: config,
|
|
||||||
rgx_lease: rgx_lease,
|
|
||||||
rgx_mac: rgx_mac,
|
|
||||||
rgx_date_start: rgx_date_start,
|
|
||||||
rgx_date_ends: rgx_date_ends,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||||
|
// ex: nmap -sV -T4 -O -F --version-light 192.168.0.1-254
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
let res = Command::new("nmap")
|
||||||
|
.args(&self.config.args)
|
||||||
|
.arg(&self.config.ip_range.clone().unwrap())
|
||||||
|
.args(vec!["-oX", "-"])
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.output()
|
||||||
|
.expect("failed to execute process");
|
||||||
|
|
||||||
nmap -sV -T4 -O -F --version-light 192.168.0.1-254
|
if res.status.success() {
|
||||||
|
let output = std::str::from_utf8(&res.stdout).expect("UTF-8 decoding error");
|
||||||
|
// println!("{}", output);
|
||||||
|
|
||||||
|
use serde_xml::value::{Content, Element};
|
||||||
|
let xml: Element = serde_xml::de::from_str(&output).expect("Bad XML format");
|
||||||
|
|
||||||
|
if let Content::Members(nodes) = xml.members {
|
||||||
|
if let Some(hosts) = nodes.get("host") {
|
||||||
|
for host_node in hosts {
|
||||||
|
if let Content::Members(host) = &host_node.members {
|
||||||
|
let address_nodes = host.get("address").unwrap();
|
||||||
|
let address = &address_nodes[0].attributes.get("addr").unwrap()[0];
|
||||||
|
let address_nodes = host.get("address").unwrap();
|
||||||
|
|
||||||
use std::fs::File;
|
println!("address={:?}", address);
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
let config_file_path = match self.config.path.len() {
|
|
||||||
0 => "/var/lib/dhcp/dhcpd.leases",
|
|
||||||
_ => &self.config.path,
|
|
||||||
};
|
|
||||||
let mut config_file =
|
|
||||||
File::open(config_file_path).expect("Could not open DHCP leases file");
|
|
||||||
let mut config_content = String::new();
|
|
||||||
config_file
|
|
||||||
.read_to_string(&mut config_content)
|
|
||||||
.expect("Could not read DHCP leases file");
|
|
||||||
|
|
||||||
let mut unauthorized_macs: Vec<String> = vec![];
|
|
||||||
|
|
||||||
for cap in self.rgx_lease.captures_iter(&config_content) {
|
|
||||||
let ip = cap.get(1).unwrap().as_str();
|
|
||||||
let content = cap.get(2).unwrap().as_str();
|
|
||||||
|
|
||||||
if let Some(mac_cap) = self.rgx_mac.captures(content) {
|
|
||||||
let mac = mac_cap.get(1).unwrap().as_str();
|
|
||||||
|
|
||||||
let starts_str = self
|
|
||||||
.rgx_date_start
|
|
||||||
.captures(content)
|
|
||||||
.expect("No 'starts' field found in lease")
|
|
||||||
.get(1)
|
|
||||||
.unwrap()
|
|
||||||
.as_str();
|
|
||||||
let starts =
|
|
||||||
chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%Y/%m/%d %H:%M:%S")
|
|
||||||
.expect(&format!("Bad date format: '{}'", starts_str));
|
|
||||||
let ends_str = self
|
|
||||||
.rgx_date_ends
|
|
||||||
.captures(content)
|
|
||||||
.expect("No 'ends' field found in lease")
|
|
||||||
.get(1)
|
|
||||||
.unwrap()
|
|
||||||
.as_str();
|
|
||||||
let ends =
|
|
||||||
chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%Y/%m/%d %H:%M:%S")
|
|
||||||
.expect(&format!("Bad date format: '{}'", ends_str));
|
|
||||||
|
|
||||||
let now = Local::now().naive_utc();
|
|
||||||
if starts <= now && now < ends {
|
|
||||||
log::debug!("Found an active DHCP lease for {}", ip);
|
|
||||||
// Lease is active
|
|
||||||
if let Some(rules) = self.config.mac_rules.get(mac) {
|
|
||||||
// Found rules
|
|
||||||
if let Some(rules_list) = rules {
|
|
||||||
// Rules contains one or more entries
|
|
||||||
for rule in rules_list {
|
|
||||||
if content.find(rule).is_none() {
|
|
||||||
unauthorized_macs.push(mac.to_owned());
|
|
||||||
|
|
||||||
sender
|
|
||||||
.send(Message {
|
|
||||||
emitter: "dhcp_leases".to_owned(),
|
|
||||||
level: Level::Issue,
|
|
||||||
msg_type: "dhcp_leases.unauthorized_mac.rule"
|
|
||||||
.to_owned(),
|
|
||||||
text: format!(
|
|
||||||
"Mismatching rule '{}' for device {} at IP {}",
|
|
||||||
rule, mac, ip
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
unauthorized_macs.push(mac.to_owned());
|
|
||||||
|
|
||||||
sender
|
|
||||||
.send(Message {
|
|
||||||
emitter: "dhcp_leases".to_owned(),
|
|
||||||
level: Level::Issue,
|
|
||||||
msg_type: "dhcp_leases.unauthorized_mac.unknown".to_owned(),
|
|
||||||
text: format!("Unknown device {} using IP {}", mac, ip),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log::warn!("No 'hardware ethernet' field found for IP {}", ip);
|
|
||||||
}
|
}
|
||||||
}
|
// println!("{:?}", );
|
||||||
|
|
||||||
if unauthorized_macs.len() > 0 {
|
|
||||||
sender
|
|
||||||
.send(Message {
|
|
||||||
emitter: "dhcp_leases".to_owned(),
|
|
||||||
level: Level::Issue,
|
|
||||||
msg_type: "dhcp_leases.unknown_mac.sumup".to_owned(),
|
|
||||||
text: format!(
|
|
||||||
"The following macs are not allowed: {:?}",
|
|
||||||
unauthorized_macs
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(
|
std::thread::sleep(std::time::Duration::from_millis(
|
||||||
|
57
src/monitors/ping.rs
Normal file
57
src/monitors/ping.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use crate::message::{Level, Message};
|
||||||
|
use crate::monitor::*;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Ping {
|
||||||
|
config: PingConfig,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PingConfig {
|
||||||
|
targets: Vec<PingConfigTarget>,
|
||||||
|
#[serde(default)]
|
||||||
|
period: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct PingConfigTarget {
|
||||||
|
host: String,
|
||||||
|
#[serde(default)]
|
||||||
|
ping_args: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
allowed_fails: Option<u64>,
|
||||||
|
#[serde(default)]
|
||||||
|
allowed_loss: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Monitor for Ping {
|
||||||
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let config = serde_yaml::from_value(config).expect("Invalid config for ping");
|
||||||
|
|
||||||
|
Ok(Ping { config: config })
|
||||||
|
}
|
||||||
|
fn run(&mut self, sender: &mpsc::Sender<Message>) {
|
||||||
|
for target in &self.config.targets {
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
let res = Command::new("ping")
|
||||||
|
.args(&target.ping_args)
|
||||||
|
.arg(&target.host)
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.expect("failed to execute process");
|
||||||
|
|
||||||
|
if !res.success() {
|
||||||
|
sender
|
||||||
|
.send(Message {
|
||||||
|
emitter: "ping".to_owned(),
|
||||||
|
msg_type: "ping.failed".to_owned(),
|
||||||
|
level: Level::Issue,
|
||||||
|
text: format!("Cannot ping {}", &target.host),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(2000))
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ struct WifiAvailabilityConfig {
|
|||||||
|
|
||||||
impl Monitor for WifiAvailability {
|
impl Monitor for WifiAvailability {
|
||||||
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let config = serde_yaml::from_value(config).expect("Invalid config for wifi_availability");
|
let config = serde_yaml::from_value(config)?;
|
||||||
|
|
||||||
let wa = WifiAvailability { config: config };
|
let wa = WifiAvailability { config: config };
|
||||||
// Activate iface
|
// Activate iface
|
||||||
|
@ -8,9 +8,9 @@ pub struct Espeak {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct EspeakConfig {
|
struct EspeakConfig {
|
||||||
#[serde(default = "espeak".to_owned())]
|
// #[serde(default = "espeak")]
|
||||||
espeak: String,
|
espeak: String,
|
||||||
#[serde(default = vec![])]
|
// #[serde(default = vec![])]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,52 +96,8 @@ impl BluetoothLightbulb {
|
|||||||
.expect("Could not start discovery session");
|
.expect("Could not start discovery session");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Output for BluetoothLightbulb {
|
fn get_targets(&self) -> Vec<(BluetoothDevice, BluetoothGATTCharacteristic)> {
|
||||||
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
|
||||||
let config: BluetoothLightbulbConfig = serde_yaml::from_value(config)?;
|
|
||||||
|
|
||||||
let msg_types = config
|
|
||||||
.msg_types
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
(
|
|
||||||
Glob::new(k)
|
|
||||||
.expect("Invalid glob pattern")
|
|
||||||
.compile_matcher(),
|
|
||||||
v.clone(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let levels = config
|
|
||||||
.levels
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let session = BluetoothSession::create_session(None)?;
|
|
||||||
|
|
||||||
let ret = BluetoothLightbulb {
|
|
||||||
config: config,
|
|
||||||
msg_types: msg_types,
|
|
||||||
levels: levels,
|
|
||||||
session: session,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize bluetooth adapter
|
|
||||||
let adapter = BluetoothAdapter::init(&ret.session)?;
|
|
||||||
// Power on
|
|
||||||
if !adapter.is_powered().unwrap() {
|
|
||||||
log::info!("bluetooth adapter {}: Set power ON", adapter.get_id());
|
|
||||||
adapter
|
|
||||||
.set_powered(true)
|
|
||||||
.expect("Cannot power on bluetooth device");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
fn process_message(&mut self, message: Message) {
|
|
||||||
let adapter =
|
let adapter =
|
||||||
BluetoothAdapter::init(&self.session).expect("Could not initialize bluetooth adapter");
|
BluetoothAdapter::init(&self.session).expect("Could not initialize bluetooth adapter");
|
||||||
|
|
||||||
@ -209,9 +165,64 @@ impl Output for BluetoothLightbulb {
|
|||||||
macs_not_found.keys()
|
macs_not_found.keys()
|
||||||
);
|
);
|
||||||
self.initiate_scan(&adapter);
|
self.initiate_scan(&adapter);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Output for BluetoothLightbulb {
|
||||||
|
fn new(config: serde_yaml::Value) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
let config: BluetoothLightbulbConfig = serde_yaml::from_value(config)?;
|
||||||
|
|
||||||
|
let msg_types = config
|
||||||
|
.msg_types
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
Glob::new(k)
|
||||||
|
.expect("Invalid glob pattern")
|
||||||
|
.compile_matcher(),
|
||||||
|
v.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let levels = config
|
||||||
|
.levels
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let session = BluetoothSession::create_session(None)?;
|
||||||
|
|
||||||
|
let ret = BluetoothLightbulb {
|
||||||
|
config: config,
|
||||||
|
msg_types: msg_types,
|
||||||
|
levels: levels,
|
||||||
|
session: session,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize bluetooth adapter
|
||||||
|
let adapter = BluetoothAdapter::init(&ret.session)?;
|
||||||
|
// Power on
|
||||||
|
if !adapter.is_powered().unwrap() {
|
||||||
|
log::info!("bluetooth adapter {}: Set power ON", adapter.get_id());
|
||||||
|
adapter
|
||||||
|
.set_powered(true)
|
||||||
|
.expect("Cannot power on bluetooth device");
|
||||||
|
}
|
||||||
|
|
||||||
|
let targets = ret.get_targets();
|
||||||
|
ret.set_color(&targets, 0.0, 1.0, 0.0);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||||
|
ret.set_color(&targets, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_message(&mut self, message: Message) {
|
||||||
|
let targets = self.get_targets();
|
||||||
|
|
||||||
// Merge LightConfig properties
|
// Merge LightConfig properties
|
||||||
let mut cfg = LightConfig {
|
let mut cfg = LightConfig {
|
||||||
color: Some(0xffffff),
|
color: Some(0xffffff),
|
||||||
|
Loading…
Reference in New Issue
Block a user