1
0
Fork 0
mirror of https://codeberg.org/icewind/mitemp-rs.git synced 2026-06-03 17:24:08 +02:00

working broadcast version

This commit is contained in:
Robin Appelman 2020-02-08 00:52:18 +01:00
commit f0cb594a33
4 changed files with 110 additions and 270 deletions

View file

@ -6,6 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
btleplug = { version = "0.4.0", path = "../btleplug" } btleplug = { version = "0.4.0", path = "../btleplug" }
num_enum = "0.4.2"
[dev-dependencies] [dev-dependencies]
main_error = "0.1.0" main_error = "0.1.0"

View file

@ -1,5 +1,17 @@
use btleplug::api::BDAddr;
use mitemp::{adapter_by_mac, listen};
use std::str::FromStr;
fn main() -> Result<(), btleplug::Error> { fn main() -> Result<(), btleplug::Error> {
env_logger::init(); env_logger::init();
mitemp::test()?;
Ok(()) let addr = BDAddr::from_str("00:1A:7D:DA:71:08").unwrap();
let adapter = adapter_by_mac(addr)?;
let device = BDAddr::from_str("58:2d:34:35:f3:d4").unwrap();
let rx = listen(adapter, device);
loop {
let data = rx.recv().unwrap();
dbg!(data);
}
} }

View file

@ -1,62 +0,0 @@
use std::ops::Rem;
pub fn mix_a(mac: [u8; 6], product_id: u16) -> [u8; 8] {
[
mac[0],
mac[2],
mac[5],
(product_id & 0xFF) as u8,
(product_id & 0xFF) as u8,
mac[4],
mac[5],
mac[1],
]
}
pub fn mix_b(mac: [u8; 6], product_id: u16) -> [u8; 8] {
[
mac[0],
mac[2],
mac[5],
(product_id >> 8 & 255) as u8,
mac[4],
mac[0],
mac[5],
(product_id & 255) as u8,
]
}
const KEY_SIZE: usize = 256;
fn cipher_init(key: &[u8]) -> [u8; KEY_SIZE] {
let mut perm = [0u8; KEY_SIZE];
for i in 0..KEY_SIZE {
perm[i] = i as u8;
}
let mut j: u8 = 0;
for ia in 0..KEY_SIZE {
j = j.wrapping_add(perm[ia]).wrapping_add(key[ia % key.len()]);
perm.swap(ia, j as usize);
}
perm
}
fn cipher_crypt(input: &[u8], mut perm: [u8; KEY_SIZE]) -> Vec<u8> {
let mut output = Vec::with_capacity(input.len());
let mut index1: u8 = 0;
let mut index2: u8 = 0;
for i in 0..input.len() {
index1 = index1.wrapping_add(1);
index2 = index2.wrapping_add(perm[index1 as usize]);
perm.swap(index1 as usize, index2 as usize);
let index = perm[index1 as usize].wrapping_add(perm[index2 as usize]);
output.push(input[i] ^ perm[index as usize]);
}
output
}
pub fn cipher(key: &[u8], input: &[u8]) -> Vec<u8> {
cipher_crypt(input, cipher_init(key))
}

View file

@ -2,39 +2,71 @@
#![allow(unused_variables)] #![allow(unused_variables)]
#![allow(unused_mut)] #![allow(unused_mut)]
mod cipher; use btleplug::api::{BDAddr, Central, CentralEvent};
use btleplug::api::{
BDAddr, Central, CentralEvent, CharPropFlags, Characteristic, Peripheral, UUID,
};
use btleplug::bluez::adapter::ConnectedAdapter; use btleplug::bluez::adapter::ConnectedAdapter;
use btleplug::bluez::manager::Manager; use btleplug::bluez::manager::Manager;
use std::str::FromStr; use btleplug::bluez::protocol::hci::LEAdvertisingData;
use std::sync::atomic::{AtomicBool, Ordering}; use num_enum::TryFromPrimitive;
use std::sync::mpsc::channel; use std::convert::TryFrom;
use std::sync::Arc; use std::sync::mpsc::{channel, Receiver};
use std::thread; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub fn test() -> Result<(), btleplug::Error> { pub fn listen(adapter: ConnectedAdapter, sensor_mac: BDAddr) -> Receiver<SensorData> {
// let addr = BDAddr::from_str("1C:4D:70:56:DA:AA").unwrap(); let (tx, rx) = channel();
let addr = BDAddr::from_str("00:1A:7D:DA:71:08").unwrap();
// let addr = BDAddr::from_str("40:4E:36:BF:E1:45").unwrap();
let adapter = adapter_by_mac(addr)?;
dbg!("discovering"); let mut sensor_data = Arc::new(Mutex::new(SensorData::default()));
let addr = BDAddr::from_str("58:2d:34:35:f3:d4").unwrap(); adapter.on_event(Box::new(move |ev| {
match ev {
CentralEvent::DeviceDiscovered(discovered_mac, data)
| CentralEvent::DeviceUpdated(discovered_mac, data)
if sensor_mac == discovered_mac =>
{
// dbg!(data);
for item in data {
match item {
LEAdvertisingData::ServiceData16(id, data) => {
if let Some(sensor_update) = parse_advertising_data(data) {
let updated = {
let mut data = sensor_data.lock().unwrap();
data.update(sensor_update);
data.clone()
};
tx.send(updated).unwrap()
}
}
_ => {}
}
}
}
_ => {}
};
}));
let mut device = Device::discover(&adapter, addr).unwrap(); adapter.start_scan().unwrap();
println!("connecting"); rx
device.connect()?; }
device.auth([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])?; #[derive(Default, Clone, Debug)]
let _ = dbg!(device.get_firmware()); pub struct SensorData {
let _ = dbg!(device.get_battery()); battery: u8,
device.listen(|| {}) temperature: f32,
humidity: f32,
}
impl SensorData {
fn update(&mut self, update: SensorUpdate) {
match update {
SensorUpdate::Temperature(temp) => self.temperature = temp,
SensorUpdate::Humidity(hum) => self.humidity = hum,
SensorUpdate::Battery(bat) => self.battery = bat,
SensorUpdate::TemperatureAndHumidity(temp, hum) => {
self.humidity = hum;
self.temperature = temp
}
}
}
} }
pub fn adapter_by_mac(addr: BDAddr) -> Result<ConnectedAdapter, btleplug::Error> { pub fn adapter_by_mac(addr: BDAddr) -> Result<ConnectedAdapter, btleplug::Error> {
@ -45,191 +77,48 @@ pub fn adapter_by_mac(addr: BDAddr) -> Result<ConnectedAdapter, btleplug::Error>
.into_iter() .into_iter()
.find(|adapter| adapter.addr == addr) .find(|adapter| adapter.addr == addr)
.ok_or(btleplug::Error::DeviceNotFound)?; .ok_or(btleplug::Error::DeviceNotFound)?;
//
// adapter = manager.down(&adapter).unwrap();
// adapter = manager.up(&adapter).unwrap();
adapter.connect() adapter.connect()
} }
const UUID_DEVICE_NAME: UUID = UUID::B16(0x2A00); #[derive(Debug, Eq, PartialEq, TryFromPrimitive, Clone, Copy)]
const UUID_APPEARANCE: UUID = UUID::B16(0x2A01); #[repr(u8)]
const UUID_BATTERY: UUID = UUID::B16(0x2A19); enum SensorType {
const UUID_MODEL: UUID = UUID::B16(0x2A24); Temperature = 0x04,
const UUID_SERIAL: UUID = UUID::B16(0x2A25); Humidity = 0x06,
const UUID_FIRMWARE: UUID = UUID::B16(0x2A26); Battery = 0x0A,
const UUID_REVISION: UUID = UUID::B16(0x2A27); TemperatureAndHumidity = 0x0D,
const UUID_MANUFACTURER: UUID = UUID::B16(0x2A29);
const UUID_WRITE_SENSOR: UUID = UUID::B16(0x2A05);
const UUID_AUTH_INIT: UUID = UUID::B16(0x0010);
const UUID_AUTH: UUID = UUID::B16(0x0001);
const UUID_AUTH_VER: UUID = UUID::B16(0x0004);
struct Device<P: Peripheral> {
peripheral: P,
connected: bool,
} }
impl<P: Peripheral> Device<P> { #[derive(Debug)]
pub fn discover<C: Central<P>>(adapter: &C, mac: BDAddr) -> Result<Self, btleplug::Error> { enum SensorUpdate {
let found = Arc::new(AtomicBool::new(false)); Battery(u8),
adapter.start_scan()?; Temperature(f32),
let device_count_clone = found.clone(); Humidity(f32),
TemperatureAndHumidity(f32, f32),
}
adapter.on_event(Box::new(move |ev| { fn parse_advertising_data(data: &[u8]) -> Option<SensorUpdate> {
match ev { const SENSOR_HUMIDITY: u8 = 0x06;
CentralEvent::DeviceDiscovered(discovered_mac) if mac == discovered_mac => { const SENSOR_TEMPERATURE: u8 = 0x04;
// println!("discovered {}", discovered_mac);
// device_count_clone.store(true, Ordering::Relaxed);
}
_ => {}
};
}));
for _ in 0..150 { let sensor_type = &data[1..4];
thread::sleep(Duration::from_millis(100)); assert_eq!(sensor_type, &[0x20, 0xaa, 0x01]);
if found.load(Ordering::Relaxed) { let sensor_type = SensorType::try_from(data[11]).ok()?;
break; let data_length = data[13] as usize;
} assert_eq!(14 + data_length, data.len());
} let sensor_data = &data[14..14 + data_length];
match sensor_type {
adapter.stop_scan()?; SensorType::Battery => Some(SensorUpdate::Battery(sensor_data[0])),
SensorType::Temperature => Some(SensorUpdate::Temperature(
match adapter.peripheral(mac) { i16::from_le_bytes([sensor_data[0], sensor_data[1]]) as f32 / 10.0,
Some(peripheral) => Ok(Device { )),
peripheral: dbg!(peripheral), SensorType::Humidity => Some(SensorUpdate::Humidity(
connected: false, u16::from_le_bytes([sensor_data[0], sensor_data[1]]) as f32 / 10.0,
}), )),
None => Err(btleplug::Error::DeviceNotFound), SensorType::TemperatureAndHumidity => Some(SensorUpdate::TemperatureAndHumidity(
} i16::from_le_bytes([sensor_data[0], sensor_data[1]]) as f32 / 10.0,
} u16::from_le_bytes([sensor_data[2], sensor_data[3]]) as f32 / 10.0,
)),
pub fn connect(&mut self) -> Result<(), btleplug::Error> {
if self.connected {
return Ok(());
}
let start = Instant::now();
for _ in 0..3 {
match self.peripheral.connect() {
Ok(_) => {
self.connected = true;
self.peripheral.discover_characteristics()?;
// dbg!(self.peripheral.characteristics());
return Ok(());
}
Err(e) => eprintln!("{}", e),
}
}
let duration = Instant::now().duration_since(start);
Err(btleplug::Error::TimedOut(duration))
}
fn get_characteristic(&self, uuid: UUID) -> Option<Characteristic> {
self.peripheral
.characteristics()
.into_iter()
.find(|c| c.uuid == uuid)
}
fn get_u8(&self, uuid: UUID) -> Result<u8, btleplug::Error> {
let c = self.get_characteristic(uuid).unwrap();
let data = self.peripheral.read(&c)?;
Ok(data[1])
}
fn get_u16(&self, uuid: UUID) -> Result<u16, btleplug::Error> {
let c = self.get_characteristic(uuid).unwrap();
let data = self.peripheral.read(&c)?;
dbg!(&data);
Ok(u16::from_le_bytes([data[1], data[2]]))
}
pub fn get_battery(&self) -> Result<u8, btleplug::Error> {
self.get_u8(UUID_BATTERY)
}
fn get_string(&self, uuid: UUID) -> Result<String, btleplug::Error> {
let c = self.get_characteristic(uuid).unwrap();
let data = self.peripheral.read(&c)?;
let data = data[1..].to_vec();
Ok(String::from_utf8(data).unwrap())
}
pub fn get_firmware(&self) -> Result<String, btleplug::Error> {
self.get_string(UUID_FIRMWARE)
}
pub fn get_name(&self) -> Result<String, btleplug::Error> {
self.get_string(UUID_DEVICE_NAME)
}
pub fn auth(&self, token: [u8; 12]) -> Result<(), btleplug::Error> {
dbg!("auth");
dbg!(self.peripheral.request(
&self.get_characteristic(UUID_AUTH_INIT).unwrap(),
// &[0x90, 0xCA, 0x85, 0xDE],
&[0x90, 0xCA, 0x85, 0xDE],
));
let (auth_in, auth_out) = channel();
self.peripheral
.subscribe(&self.get_characteristic(UUID_AUTH).unwrap())?;
self.peripheral
.on_notification(Box::new(move |notification| {
dbg!(notification.uuid);
if notification.uuid == UUID_AUTH {
auth_in.send(notification.value).unwrap();
}
}));
let mac = self.peripheral.address().address;
let device_id = 426;
// let device_id = 131;
let data = cipher::cipher(&cipher::mix_a(mac, device_id), &token);
self.peripheral
.request(&self.get_characteristic(UUID_AUTH).unwrap(), &data);
let auth_response = auth_out.recv().unwrap();
let verify = cipher::cipher(
&cipher::mix_b(mac, device_id),
&cipher::cipher(&cipher::mix_a(mac, device_id), &auth_response),
);
assert_eq!(&token, verify.as_slice());
// dbg!(self.peripheral.command(
// &self.get_characteristic(UUID_AUTH).unwrap(),
// &[0x92, 0xAB, 0x54, 0xFA],
// ));
dbg!(self.peripheral.request(
&self.get_characteristic(UUID_AUTH).unwrap(),
&[0x92, 0xAB, 0x54, 0xFA],
));
dbg!(&self
.peripheral
.read(&self.get_characteristic(UUID_AUTH_VER).unwrap())?);
Ok(())
}
pub fn listen(&self, callback: impl Fn() -> ()) -> Result<(), btleplug::Error> {
let uuid = UUID::from_str("22:6C:AA:55:64:76:45:66:75:62:66:73:44:70:66:6D").unwrap();
let sensor = self.get_characteristic(uuid).unwrap();
eprintln!("sub");
self.peripheral.subscribe(&sensor)?;
eprintln!("notify");
self.peripheral.on_notification(Box::new(|notification| {
dbg!(String::from_utf8(notification.value).unwrap());
}));
loop {
eprintln!("sleep");
thread::sleep(Duration::from_millis(5000));
dbg!(self.get_battery());
}
} }
} }