1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::db::Db;
use crate::reading::{Message, Reading, Type};
use crate::services::Service;
use crate::threading;
use crate::value::Value;
use crate::Result;
use bus::Bus;
use chrono::Local;
use crossbeam_channel::Sender;
use eventsource::reqwest::Client;
use rouille::url::Url;
use serde::Deserialize;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
const URL: &str = "https://developer-api.nest.com";
pub struct Nest {
service_id: String,
token: String,
}
#[derive(Deserialize, Debug, Clone)]
pub struct Settings {
token: String,
}
impl Nest {
pub fn new(service_id: &str, settings: &Settings) -> Nest {
Nest {
service_id: service_id.into(),
token: settings.token.clone(),
}
}
}
impl Service for Nest {
fn spawn(self: Box<Self>, _db: Arc<Mutex<Db>>, tx: &Sender<Message>, _rx: &mut Bus<Message>) -> Result<()> {
let tx = tx.clone();
threading::spawn(format!("my-iot::nest:{}", &self.service_id), move || loop {
let client = Client::new(Url::parse_with_params(URL, &[("auth", &self.token)]).unwrap());
for event in client {
if let Ok(event) = event {
if let Some(event_type) = event.event_type {
if event_type == "put" {
self.send_readings(&serde_json::from_str(&event.data).unwrap(), &tx)
.unwrap();
}
}
}
}
})?;
Ok(())
}
}
impl Nest {
fn send_readings(&self, event: &NestEvent, tx: &Sender<Message>) -> Result<()> {
let now = Local::now();
for (id, thermostat) in event.data.devices.thermostats.iter() {
tx.try_send(Message {
type_: Type::Actual,
reading: Reading {
sensor: format!("{}::thermostat::{}::ambient_temperature", &self.service_id, &id),
value: Value::Celsius(thermostat.ambient_temperature_c),
timestamp: now,
},
})?;
tx.try_send(Message {
type_: Type::Actual,
reading: Reading {
sensor: format!("{}::thermostat::{}::humidity", &self.service_id, &id),
value: Value::Rh(thermostat.humidity),
timestamp: now,
},
})?;
}
for (id, camera) in event.data.devices.cameras.iter() {
tx.try_send(Message {
type_: Type::Actual,
reading: Reading {
sensor: format!("{}::camera::{}::snapshot_url", &self.service_id, &id),
value: Value::ImageUrl(camera.snapshot_url.clone()),
timestamp: now,
},
})?;
}
Ok(())
}
}
#[derive(Deserialize, Debug)]
struct NestEvent {
data: NestData,
}
#[derive(Deserialize, Debug)]
struct NestData {
devices: NestDevices,
}
#[derive(Deserialize, Debug)]
struct NestDevices {
thermostats: HashMap<String, NestThermostat>,
cameras: HashMap<String, NestCamera>,
}
#[derive(Deserialize, Debug)]
struct NestThermostat {
ambient_temperature_c: f64,
humidity: f64,
}
#[derive(Deserialize, Debug)]
struct NestCamera {
snapshot_url: String,
}