#[derive(Debug, Clone)]
pub struct Appartement {
inner: std::sync::Arc<std::sync::Mutex<InnerAppartement>>,
}
impl Appartement {
pub fn connect<S>(host: S, user: S, password: S) -> Result<Appartement>
where
S: Into<String>,
{
let appt = Appartement {
inner: std::sync::Arc::new(std::sync::Mutex::new(InnerAppartement {
api: RawApi::connect(host, user, password)?,
zones: vec![],
thread: std::sync::Arc::new(std::sync::Mutex::new(false)),
})),
};
appt.inner.lock()?.update_structure()?;
Ok(appt)
}
pub fn get_zones(&self) -> Result<Vec<Zone>> {
Ok(self.inner.lock()?.zones.clone())
}
pub fn update_all(&self) -> Result<Vec<Zone>> {
self.inner.lock()?.update_structure()?;
Ok(self.inner.lock()?.zones.clone())
}
pub fn set_value(&self, zone: usize, group: Option<usize>, value: Value) -> Result<()> {
self.inner.lock()?.set_value(zone, group, value)
}
pub fn event_channel(&self) -> Result<std::sync::mpsc::Receiver<Event>> {
if *self.inner.lock()?.thread.lock()? == true {
*self.inner.lock()?.thread.lock()? = false;
}
let (recv, status) = self.inner.lock()?.api.new_event_channel()?;
let (inp, out) = std::sync::mpsc::channel();
let internal_status = status.clone();
let appr = self.inner.clone();
std::thread::spawn(move || loop {
let event = match recv.recv() {
Ok(e) => e,
Err(_) => break,
};
if *internal_status.lock().unwrap() == false {
break;
}
let events = appr.lock().unwrap().expand_value(event).unwrap();
for event in events {
let event = appr.lock().unwrap().update_event_value(event).unwrap();
appr.lock().unwrap().zones.iter_mut().for_each(|z| {
if z.id == event.zone {
z.groups.iter_mut().for_each(|g| {
if g.typ == event.typ && g.id == event.group {
g.status = event.value.clone();
}
});
}
});
match inp.send(event) {
Ok(_) => {}
Err(_) => break,
}
}
});
self.inner.lock()?.thread = status;
Ok(out)
}
}
#[derive(Debug)]
struct InnerAppartement {
api: RawApi,
zones: Vec<Zone>,
thread: std::sync::Arc<std::sync::Mutex<bool>>,
}
impl InnerAppartement {
fn set_value(&mut self, zone: usize, group: Option<usize>, value: Value) -> Result<()> {
if let Some(grp) = group {
match value {
Value::Light(light) => {
if light < 0.5 {
self.api.call_action(zone, Action::LightOff(grp))?;
} else {
self.api.call_action(zone, Action::LightOn(grp))?;
}
}
Value::Shadow(open, angle) => {
if open <= 0.1 {
self.api.call_action(zone, Action::ShadowUp(grp))?;
}
if open >= 0.9 && angle <= 0.1 {
self.api.call_action(zone, Action::ShadowDown(grp))?;
} else {
let devices = self
.zones
.iter()
.find(|z| z.id == zone)
.ok_or("Not a valid zone id given")?
.groups
.iter()
.find(|g| g.id == grp)
.ok_or("Not a valid group given")?
.devices
.iter()
.filter(|d| d.device_type == DeviceType::Shadow)
.clone();
for dev in devices {
self.api.set_shadow_device_open(dev.id.clone(), open)?;
self.api.set_shadow_device_angle(dev.id.clone(), angle)?;
}
self.zones
.iter_mut()
.find(|z| z.id == zone)
.ok_or("No valid zone given")?
.groups
.iter_mut()
.find(|g| g.id == grp)
.ok_or("Not a valid group given")?
.status = value;
}
}
Value::Unknown => (),
}
}
else {
match value {
Value::Light(light) => {
if light < 0.5 {
self.api.call_action(zone, Action::AllLightOff)?;
} else {
self.api.call_action(zone, Action::AllLightOn)?;
}
}
Value::Shadow(open, angle) => {
if open <= 0.1 {
self.api.call_action(zone, Action::AllShadowUp)?;
}
if open >= 0.9 && angle <= 0.1 {
self.api.call_action(zone, Action::AllShadowDown)?;
} else {
let devices = self
.zones
.iter()
.find(|z| z.id == zone)
.ok_or("Not a valid zone id given")?
.groups
.iter()
.map(|g| g.devices.clone())
.flatten()
.collect::<Vec<Device>>()
.into_iter()
.filter(|d| d.device_type == DeviceType::Shadow);
for dev in devices {
self.api.set_shadow_device_open(dev.id.clone(), open)?;
self.api.set_shadow_device_angle(dev.id.clone(), angle)?;
}
self.zones
.iter_mut()
.find(|z| z.id == zone)
.ok_or("Not a valid zone id given")?
.groups
.iter_mut()
.filter(|g| g.typ == Type::Shadow)
.for_each(|g| g.status = value.clone());
}
}
Value::Unknown => (),
}
}
Ok(())
}
fn expand_value(&self, event: Event) -> Result<Vec<Event>> {
if event.typ == Type::Shadow
&& (event.action == Action::ShadowStepOpen || event.action == Action::ShadowStepClose)
{
let groups: Vec<usize> = self
.zones
.iter()
.find(|z| z.id == event.zone)
.ok_or("No matching zone available")?
.groups
.iter()
.filter(|g| g.typ == Type::Shadow)
.map(|g| g.id)
.collect();
return Ok(groups
.iter()
.map(|g| {
let mut e = event.clone();
e.group = *g;
e
})
.collect());
}
Ok(vec![event])
}
fn update_event_value(&self, event: Event) -> Result<Event> {
let mut event = event;
event.value = self.update_value(event.value, &event.typ, event.zone, event.group)?;
Ok(event)
}
fn update_value(&self, value: Value, typ: &Type, zone: usize, group: usize) -> Result<Value> {
if value != Value::Unknown {
return Ok(value);
}
if typ == &Type::Shadow {
let device = self
.zones
.iter()
.find(|z| z.id == zone)
.ok_or("No matching zone found")?
.groups
.iter()
.find(|g| g.id == group && g.typ == Type::Shadow)
.ok_or("No matching group found")?
.devices
.get(0)
.ok_or("No devices available")?;
let open = self.api.get_shadow_device_open(&device.id)?;
let angle = self.api.get_shadow_device_angle(&device.id)?;
return Ok(Value::Shadow(open, angle));
}
Ok(value)
}
fn update_structure(&mut self) -> Result<()> {
let devices = self.api.get_devices()?;
let mut zones: Vec<Zone> = self
.api
.get_zones()?
.into_iter()
.filter(|z| z.id != 0 && z.id != 65534)
.collect();
for zone in &mut zones {
for typ in &zone.types {
let scenes = self.api.get_scenes(zone.id, typ.clone())?;
let mut scene_groups = Group::from_scenes(&scenes, zone.id, &typ);
let action;
if typ == &Type::Shadow {
action = Action::Unknown;
} else {
let lcs = self.api.get_last_called_scene(zone.id, typ.clone())?;
action = Action::new(typ.clone(), lcs);
}
scene_groups
.iter_mut()
.for_each(|g| g.status = Value::from_action(action.clone(), g.id));
zone.groups.append(&mut scene_groups);
}
for group in &mut zone.groups {
for device in devices.iter().filter(|d| {
d.device_type == DeviceType::Light || d.device_type == DeviceType::Shadow
}) {
if group.id == 0
&& device.zone_id == group.zone_id
&& group.typ == device.button_type
{
group.devices.push(device.clone());
}
else if device.zone_id == group.zone_id && group.typ == device.button_type {
let _ = self
.api
.get_device_scene_mode(device.id.clone(), group.id)
.and_then(|dsm| {
if !dsm.dont_care {
group.devices.push(device.clone());
}
Ok(())
});
}
}
}
}
self.zones = zones.clone();
for zone in &mut zones {
for group in zone.groups.iter_mut().filter(|g| g.typ == Type::Shadow) {
let status = self.update_value(group.status.clone(), &group.typ, zone.id, group.id);
match status {
Ok(v) => group.status = v,
Err(_) => continue,
}
}
}
self.zones = zones;
Ok(())
}
}
impl Drop for InnerAppartement {
fn drop(&mut self) {
#[allow(unused_must_use)]
{
self.thread.lock().map(|mut v| *v = false);
}
}
}
#[derive(Debug, Clone)]
pub struct RawApi {
host: String,
user: String,
password: String,
token: String,
}
impl RawApi {
pub fn connect<S>(host: S, user: S, password: S) -> Result<Self>
where
S: Into<String>,
{
let mut api = RawApi {
host: host.into(),
user: user.into(),
password: password.into(),
token: String::from(""),
};
api.login()?;
Ok(api)
}
fn login(&mut self) -> Result<()> {
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()?;
let mut response = client
.get(&format!("https://{}:8080/json/system/login", self.host))
.query(&[("user", &self.user), ("password", &self.password)])
.send()?;
let json: serde_json::Value = response.json()?;
self.token = json
.get("result")
.ok_or("No result in Json response")?
.get("token")
.ok_or("No token in Json response")?
.as_str()
.ok_or("Token is not a String")?
.to_string();
Ok(())
}
pub fn generic_request<S>(
&self,
request: S,
parameter: Option<Vec<(&str, &str)>>,
) -> Result<serde_json::Value>
where
S: Into<String>,
{
let parameter = match parameter {
None => vec![("token", self.token.as_str())],
Some(mut p) => {
p.push(("token", &self.token));
p
}
};
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.timeout(None)
.build()?;
let mut response = client
.get(&format!(
"https://{}:8080/json/{}",
self.host,
request.into()
))
.query(¶meter)
.send()?;
let mut json: serde_json::Value = response.json()?;
if !json
.get("ok")
.ok_or("No ok in Json response")?
.as_bool()
.ok_or("No boolean ok code")?
{
return Err("Request failed, no ok code received".into());
}
match json.get_mut("result") {
None => Ok(serde_json::json!(null)),
Some(j) => Ok(j.take()),
}
}
pub fn new_event_channel(
&self,
) -> Result<(
std::sync::mpsc::Receiver<Event>,
std::sync::Arc<std::sync::Mutex<bool>>,
)> {
let thread_status = std::sync::Arc::new(std::sync::Mutex::new(true));
self.generic_request(
"event/subscribe",
Some(vec![("name", "callScene"), ("subscriptionID", "911")]),
)?;
let (send, recv) = std::sync::mpsc::channel();
let this = self.clone();
let ts = thread_status.clone();
std::thread::spawn(move || loop {
let res = this.generic_request(
"event/get",
Some(vec![("timeout", "3000"), ("subscriptionID", "911")]),
);
if *ts.lock().unwrap() == false {
break;
}
#[allow(unused_must_use)]
{
send.send(res);
}
});
let (inp, out) = std::sync::mpsc::channel();
let this = self.clone();
let ts = thread_status.clone();
std::thread::spawn(move || loop {
#[allow(unused_must_use)]
{
let res = recv.recv();
if *ts.lock().unwrap() == false {
break;
}
res.and_then(|res| {
res.and_then(|mut v| {
this.extract_events(&mut v).and_then(|es| {
es.into_iter().for_each(|e| {
let _tmp = inp.send(e);
});
Ok(())
});
Ok(())
});
Ok(())
});
}
});
Ok((out, thread_status))
}
fn extract_events(&self, json: &mut serde_json::Value) -> Result<Vec<Event>> {
let events = json
.get_mut("events")
.ok_or("No events available")?
.as_array_mut()
.take()
.ok_or("Events not in array")?;
let mut out = vec![];
for e in events {
let name = e
.get("name")
.ok_or("No name for event")?
.as_str()
.ok_or("Event name not a string")?
.to_string();
let props = e
.get_mut("properties")
.ok_or("No properties in event")?
.take();
let mut event: Event = serde_json::from_value(props)?;
event.name = name;
event.action = Action::from(event.clone());
event.group = Group::group_id_from_scene_id(event.scene);
event.value = Value::from_action(event.action.clone(), event.group);
out.push(event);
}
Ok(out)
}
pub fn get_appartement_name(&self) -> Result<String> {
Ok(self
.generic_request("apartment/getName", None)?
.get("result")
.ok_or("No result in Json response")?
.get("name")
.ok_or("No name in Json response")?
.as_str()
.ok_or("Name is not a String")?
.to_string())
}
pub fn set_appartement_name<S>(&self, new_name: S) -> Result<bool>
where
S: Into<String>,
{
Ok(self
.generic_request(
"apartment/getName",
Some(vec![("newName", &new_name.into())]),
)?
.get("ok")
.ok_or("No ok in Json response")?
.as_bool()
.ok_or("No boolean ok code")?)
}
pub fn get_zones(&self) -> Result<Vec<Zone>> {
let mut json = self.generic_request("apartment/getReachableGroups", None)?;
let json = json
.get_mut("zones")
.ok_or("No zones in Json response")?
.take();
Ok(serde_json::from_value(json)?)
}
pub fn get_zone_name(&self, id: usize) -> Result<String> {
let res = self.generic_request("zone/getName", Some(vec![("id", &id.to_string())]))?;
let name = res
.get("name")
.ok_or("No name returned")?
.as_str()
.ok_or("No String value available")?;
Ok(name.to_string())
}
pub fn get_devices(&self) -> Result<Vec<Device>> {
let res = self.generic_request("apartment/getDevices", None)?;
Ok(serde_json::from_value(res)?)
}
pub fn get_device_scene_mode<S>(&self, device: S, scene_id: usize) -> Result<SceneMode>
where
S: Into<String>,
{
let json = self.generic_request(
"device/getSceneMode",
Some(vec![
("dsid", &device.into()),
("sceneID", &scene_id.to_string()),
]),
)?;
Ok(serde_json::from_value(json)?)
}
pub fn get_circuits(&self) -> Result<Vec<Circut>> {
let mut res = self.generic_request("apartment/getCircuits", None)?;
let res = res
.get_mut("circuits")
.ok_or("No circuits available")?
.take();
Ok(serde_json::from_value(res)?)
}
pub fn get_scenes(&self, zone: usize, typ: Type) -> Result<Vec<usize>> {
let typ = typ as usize;
let mut json = self.generic_request(
"zone/getReachableScenes",
Some(vec![
("id", &zone.to_string()),
("groupID", &typ.to_string()),
]),
)?;
let json = json
.get_mut("reachableScenes")
.ok_or("No scenes returned")?
.take();
Ok(serde_json::from_value(json)?)
}
pub fn get_last_called_scene(&self, zone: usize, typ: Type) -> Result<usize> {
let typ = typ as usize;
let res = self.generic_request(
"zone/getLastCalledScene",
Some(vec![
("id", &zone.to_string()),
("groupID", &typ.to_string()),
]),
)?;
let number = res
.get("scene")
.ok_or("No scene returned")?
.as_u64()
.ok_or("No scene number available")?;
Ok(number as usize)
}
pub fn call_scene(&self, zone: usize, typ: Type, scene: usize) -> Result<()> {
let typ = typ as usize;
self.generic_request(
"zone/callScene",
Some(vec![
("id", &zone.to_string()),
("groupID", &typ.to_string()),
("sceneNumber", &scene.to_string()),
]),
)?;
Ok(())
}
pub fn call_action(&self, zone: usize, action: Action) -> Result<()> {
let (typ, scene) = action
.to_scene_type()
.ok_or("Action can't be transformed to scene command")?;
self.call_scene(zone, typ, scene)
}
pub fn get_shadow_device_open<S>(&self, device: S) -> Result<f32>
where
S: Into<String>,
{
let res = self.generic_request(
"device/getOutputValue",
Some(vec![("dsid", &device.into()), ("offset", "2")]),
)?;
if res
.get("offset")
.ok_or("No offset returnes")?
.as_u64()
.ok_or("The offset is not a number")?
!= 2
{
return Err(Error::from("Wrong offset returned"));
}
let value = res
.get("value")
.ok_or("No value returnes")?
.as_u64()
.ok_or("The value is not a number")?;
let value = (value as f32) / 65535.0;
Ok(1.0 - value)
}
pub fn set_shadow_device_open<S>(&self, device: S, value: f32) -> Result<()>
where
S: Into<String>,
{
let value = value.max(0.0);
let value = value.min(1.0);
let value = 1.0 - value;
let value = (65535.0 * value) as usize;
self.generic_request(
"device/setOutputValue",
Some(vec![
("dsid", &device.into()),
("value", &format!("{}", value)),
("offset", "2"),
]),
)?;
Ok(())
}
pub fn get_shadow_device_angle<S>(&self, device: S) -> Result<f32>
where
S: Into<String>,
{
let res = self.generic_request(
"device/getOutputValue",
Some(vec![("dsid", &device.into()), ("offset", "4")]),
)?;
if res
.get("offset")
.ok_or("No offset returnes")?
.as_u64()
.ok_or("The offset is not a number")?
!= 4
{
return Err(Error::from("Wrong offset returned"));
}
let value = res
.get("value")
.ok_or("No value returnes")?
.as_u64()
.ok_or("The value is not a number")?;
let value = (value as f32) / 65535.0;
Ok(value)
}
pub fn set_shadow_device_angle<S>(&self, device: S, value: f32) -> Result<()>
where
S: Into<String>,
{
let value = value.max(0.0);
let value = value.min(1.0);
let value = (255.0 * value) as usize;
self.generic_request(
"device/setOutputValue",
Some(vec![
("dsid", &device.into()),
("value", &format!("{}", value)),
("offset", "4"),
]),
)?;
Ok(())
}
}
fn from_str<'de, T, D>(deserializer: D) -> std::result::Result<T, D::Error>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let s = String::deserialize(deserializer)?;
T::from_str(&s).map_err(serde::de::Error::custom)
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Event {
#[serde(default)]
pub name: String,
#[serde(alias = "zoneID", deserialize_with = "from_str")]
pub zone: usize,
#[serde(alias = "groupID", deserialize_with = "from_str")]
pub typ: Type,
#[serde(alias = "sceneID", deserialize_with = "from_str")]
pub scene: usize,
#[serde(alias = "originToken")]
pub token: String,
#[serde(alias = "originDSUID")]
pub dsuid: String,
#[serde(alias = "callOrigin")]
pub origin: String,
#[serde(default)]
pub action: Action,
#[serde(default)]
pub value: Value,
#[serde(default)]
pub group: usize,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Zone {
#[serde(alias = "zoneID")]
pub id: usize,
pub name: String,
#[serde(alias = "groups")]
pub types: Vec<Type>,
#[serde(default)]
pub groups: Vec<Group>,
}
#[derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr, PartialEq, Debug, Clone)]
#[repr(u8)]
pub enum Type {
Unknown = 0,
Light = 1,
Shadow = 2,
Heating = 3,
Audio = 4,
Video = 5,
Joker = 8,
Cooling = 9,
Ventilation = 10,
Window = 11,
AirRecirculation = 12,
TemperatureControl = 48,
ApartmentVentilation = 64,
}
impl From<u8> for Type {
fn from(u: u8) -> Self {
match u {
1 => Type::Light,
2 => Type::Shadow,
3 => Type::Heating,
4 => Type::Audio,
5 => Type::Video,
8 => Type::Joker,
9 => Type::Cooling,
10 => Type::Ventilation,
11 => Type::Window,
12 => Type::AirRecirculation,
48 => Type::TemperatureControl,
64 => Type::ApartmentVentilation,
_ => Type::Unknown,
}
}
}
impl std::str::FromStr for Type {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let u = u8::from_str(s)?;
Ok(Type::from(u))
}
}
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)]
pub enum Action {
AllLightOn,
AllLightOff,
LightOn(usize),
LightOff(usize),
AllShadowUp,
AllShadowDown,
ShadowUp(usize),
ShadowDown(usize),
AllShadowStop,
ShadowStop(usize),
ShadowStepOpen,
ShadowStepClose,
AllShadowSpecial1,
AllShadowSpecial2,
Unknown,
}
impl Action {
fn new(typ: Type, scene: usize) -> Action {
if typ == Type::Light && scene == 0 {
return Action::AllLightOff;
}
if typ == Type::Light && scene == 5 {
return Action::AllLightOn;
}
if typ == Type::Shadow && scene == 0 {
return Action::AllShadowDown;
}
if typ == Type::Shadow && scene == 5 {
return Action::AllShadowUp;
}
if scene > 0 && scene < 5 {
if typ == Type::Light {
return Action::LightOff(scene);
} else if typ == Type::Shadow {
return Action::ShadowDown(scene);
}
}
if scene > 5 && scene < 9 {
if typ == Type::Light {
return Action::LightOn(scene - 5);
} else if typ == Type::Shadow {
return Action::ShadowUp(scene - 5);
}
}
if typ == Type::Shadow && scene == 55 {
return Action::AllShadowStop;
}
if typ == Type::Shadow && scene > 50 && scene < 55 {
return Action::ShadowStop(scene - 51);
}
if typ == Type::Shadow && scene == 42 {
return Action::ShadowStepClose;
}
if typ == Type::Shadow && scene == 43 {
return Action::ShadowStepOpen;
}
if typ == Type::Shadow && scene == 17 {
return Action::AllShadowUp;
}
if typ == Type::Shadow && scene == 18 {
return Action::AllShadowSpecial1;
}
if typ == Type::Shadow && scene == 19 {
return Action::AllShadowSpecial2;
}
Action::Unknown
}
fn to_scene_type(&self) -> Option<(Type, usize)> {
match self {
Action::AllLightOff => Some((Type::Light, 0)),
Action::AllLightOn => Some((Type::Light, 5)),
Action::LightOff(v) => Some((Type::Light, *v)),
Action::LightOn(v) => Some((Type::Light, v + 5)),
Action::AllShadowDown => Some((Type::Shadow, 0)),
Action::AllShadowUp => Some((Type::Shadow, 5)),
Action::AllShadowStop => Some((Type::Shadow, 55)),
Action::AllShadowSpecial1 => Some((Type::Shadow, 18)),
Action::AllShadowSpecial2 => Some((Type::Shadow, 19)),
Action::ShadowDown(v) => Some((Type::Shadow, *v)),
Action::ShadowUp(v) => Some((Type::Shadow, v + 5)),
Action::ShadowStop(v) => Some((Type::Shadow, v + 51)),
Action::ShadowStepClose => Some((Type::Shadow, 42)),
Action::ShadowStepOpen => Some((Type::Shadow, 43)),
Action::Unknown => None,
}
}
}
impl Default for Action {
fn default() -> Self {
Action::Unknown
}
}
impl From<Event> for Action {
fn from(e: Event) -> Self {
Action::new(e.typ, e.scene)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub enum Value {
Light(f32),
Shadow(f32, f32),
Unknown,
}
impl Value {
pub fn from_action(action: Action, _id: usize) -> Self {
match action {
Action::AllLightOn => Value::Light(1.0),
Action::LightOn(_id) => Value::Light(1.0),
Action::AllLightOff => Value::Light(0.0),
Action::LightOff(_id) => Value::Light(0.0),
Action::AllShadowUp => Value::Shadow(0.0, 1.0),
Action::AllShadowDown => Value::Shadow(1.0, 0.0),
Action::ShadowDown(_id) => Value::Shadow(1.0, 0.0),
Action::ShadowUp(_id) => Value::Shadow(0.0, 1.0),
_ => Value::Unknown,
}
}
}
impl Default for Value {
fn default() -> Self {
Value::Unknown
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Device {
pub id: String,
pub name: String,
#[serde(alias = "zoneID")]
pub zone_id: usize,
#[serde(alias = "isPresent")]
pub present: bool,
#[serde(alias = "outputMode")]
pub device_type: DeviceType,
#[serde(alias = "groups")]
pub types: Vec<Type>,
#[serde(alias = "buttonActiveGroup")]
pub button_type: Type,
}
#[derive(Debug, Clone, serde::Serialize, PartialEq)]
pub enum DeviceType {
Switch,
Light,
Tv,
Shadow,
Unknown,
}
impl From<usize> for DeviceType {
fn from(num: usize) -> Self {
match num {
0 => DeviceType::Switch,
16 | 22 | 35 => DeviceType::Light,
33 => DeviceType::Shadow,
39 => DeviceType::Tv,
_ => DeviceType::Unknown,
}
}
}
impl<'de> serde::Deserialize<'de> for DeviceType {
fn deserialize<D>(deserializer: D) -> std::result::Result<DeviceType, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(DeviceType::from(usize::deserialize(deserializer)?))
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Circut {
#[serde(alias = "dsid")]
pub id: String,
pub name: String,
#[serde(alias = "isPresent")]
pub present: bool,
#[serde(alias = "isValid")]
pub valid: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SceneMode {
#[serde(alias = "sceneID")]
pub scene: usize,
#[serde(alias = "dontCare")]
pub dont_care: bool,
#[serde(alias = "localPrio")]
pub local_prio: bool,
#[serde(alias = "specialMode")]
pub special_mode: bool,
#[serde(alias = "flashMode")]
pub flash_mode: bool,
#[serde(alias = "ledconIndex")]
pub led_con_index: usize,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Group {
id: usize,
zone_id: usize,
typ: Type,
status: Value,
devices: Vec<Device>,
}
impl Group {
pub fn new(id: usize, zone_id: usize, typ: Type) -> Self {
Group {
id,
zone_id: zone_id,
typ,
devices: vec![],
status: Value::default(),
}
}
pub fn group_id_from_scene_id(scene: usize) -> usize {
if scene > 0 && scene < 5 {
return scene;
}
if scene > 5 && scene < 9 {
return scene - 5;
}
if scene > 50 && scene < 55 {
return scene - 51;
}
0
}
pub fn from_scene(scene: usize, zone_id: usize, typ: &Type) -> Option<Group> {
for x in 1..4 {
if scene == x {
return Some(Group::new(x, zone_id, typ.clone()));
}
}
if scene == 0 {
return Some(Group::new(0, zone_id, typ.clone()));
}
None
}
pub fn from_scenes(scenes: &[usize], zone_id: usize, typ: &Type) -> Vec<Group> {
let groups: Vec<Group> = scenes
.iter()
.filter_map(|s| Group::from_scene(*s, zone_id, typ))
.collect();
if groups.len() > 1 {
return groups.into_iter().filter(|g| g.id > 0).collect();
}
return groups;
}
}
impl Default for Group {
fn default() -> Self {
Group {
id: 0,
zone_id: 0,
typ: Type::Unknown,
devices: vec![],
status: Value::default(),
}
}
}
#[derive(Debug)]
pub enum Error {
Error(String),
SerdeJson(serde_json::Error),
Reqwest(reqwest::Error),
}
type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::Error(s) => write!(f, "{}", s),
Error::SerdeJson(ref e) => e.fmt(f),
Error::Reqwest(ref e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match self {
Error::Error(s) => &s,
Error::SerdeJson(ref e) => e.description(),
Error::Reqwest(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match self {
Error::Error(_) => None,
Error::SerdeJson(ref e) => Some(e),
Error::Reqwest(ref e) => Some(e),
}
}
}
impl From<&str> for Error {
fn from(err: &str) -> Self {
Error::Error(err.into())
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::SerdeJson(err)
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::Reqwest(err)
}
}
impl<T> From<std::sync::PoisonError<T>> for Error {
fn from(_err: std::sync::PoisonError<T>) -> Self {
Error::from("Poison error")
}
}