use crate::resource::{self, Creator, Modifier, RequestMethod, Scanner};
use crate::{response::Modified, Response, Result};
use serde::de::DeserializeOwned;
use serde_json::Value as JsonValue;
use std::{collections::HashMap, net::IpAddr};
#[cfg(feature = "upnp-description")]
mod description;
mod discover;
mod register;
#[cfg(feature = "upnp-description")]
pub use description::{
description, Description, DescriptionDevice, DescriptionIcon, DescriptionSpecVersion,
};
pub use discover::discover_nupnp;
pub use register::{register_user, register_user_with_clientkey};
type ResponsesModified = Vec<Response<Modified>>;
fn parse_response<T>(response: JsonValue) -> crate::Result<T>
where
T: DeserializeOwned,
{
if let Ok(mut v) = serde_json::from_value::<Vec<Response<JsonValue>>>(response.clone()) {
if let Some(v) = v.pop() {
v.into_result()?;
}
}
Ok(serde_json::from_value(response)?)
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Bridge {
username: String,
ip_address: IpAddr,
api_url: String,
}
impl Bridge {
pub fn new<S>(ip_address: IpAddr, username: S) -> Self
where
S: Into<String>,
{
let username = username.into();
Bridge {
api_url: format!("http://{}/api/{}", ip_address, username),
username,
ip_address,
}
}
pub fn username(&self) -> &str {
&self.username
}
pub fn ip_address(&self) -> &IpAddr {
&self.ip_address
}
pub(crate) fn api_request<S, T>(
&self,
url_suffix: S,
request_method: RequestMethod,
body: Option<JsonValue>,
) -> Result<T>
where
S: AsRef<str>,
T: DeserializeOwned,
{
let url = format!("{}/{}", self.api_url, url_suffix.as_ref());
let request = match request_method {
RequestMethod::Put => ureq::put(&url),
RequestMethod::Post => ureq::post(&url),
RequestMethod::Get => ureq::get(&url),
RequestMethod::Delete => ureq::delete(&url),
};
let response = match body {
Some(v) => request.send_json(v)?,
None => request.call()?,
};
Ok(response.into_json()?)
}
pub fn set_config(&self, modifier: &resource::config::Modifier) -> Result<ResponsesModified> {
modifier.execute(self, ())
}
pub fn get_config(&self) -> Result<resource::Config> {
parse_response(self.api_request("config", RequestMethod::Get, None)?)
}
pub fn set_light_attribute<S>(
&self,
id: S,
modifier: &resource::light::AttributeModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn set_light_state<S>(
&self,
id: S,
modifier: &resource::light::StateModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_light<S>(&self, id: S) -> Result<resource::Light>
where
S: Into<String>,
{
let id = id.into();
let light: resource::Light = parse_response(self.api_request(
format!("lights/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(light.with_id(id))
}
pub fn get_all_lights(&self) -> Result<Vec<resource::Light>> {
let map: HashMap<String, resource::Light> =
parse_response(self.api_request("lights", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, light)| light.with_id(id))
.collect())
}
pub fn search_new_lights(&self, scanner: &resource::light::Scanner) -> Result<()> {
scanner.execute(self)
}
pub fn get_new_lights(&self) -> Result<resource::Scan> {
parse_response(self.api_request("lights/new", RequestMethod::Get, None)?)
}
pub fn delete_light<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("lights/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn create_group(&self, creator: &resource::group::Creator) -> Result<String> {
creator.execute(self)
}
pub fn set_group_attribute<S>(
&self,
id: S,
modifier: &resource::group::AttributeModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn set_group_state<S>(
&self,
id: S,
modifier: &resource::group::StateModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_group<S>(&self, id: S) -> Result<resource::Group>
where
S: Into<String>,
{
let id = id.into();
let group: resource::Group = parse_response(self.api_request(
format!("groups/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(group.with_id(id))
}
pub fn get_all_groups(&self) -> Result<Vec<resource::Group>> {
let map: HashMap<String, resource::Group> =
parse_response(self.api_request("groups", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, group)| group.with_id(id))
.collect())
}
pub fn delete_group<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("groups/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn create_scene(&self, creator: &resource::scene::Creator) -> Result<String> {
creator.execute(self)
}
pub fn set_scene<S>(
&self,
id: S,
modifier: &resource::scene::Modifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_scene<S>(&self, id: S) -> Result<resource::Scene>
where
S: Into<String>,
{
let id = id.into();
let scene: resource::Scene = parse_response(self.api_request(
format!("scenes/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(scene.with_id(id))
}
pub fn get_all_scenes(&self) -> Result<Vec<resource::Scene>> {
let map: HashMap<String, resource::Scene> =
parse_response(self.api_request("scenes", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, scene)| scene.with_id(id))
.collect())
}
pub fn delete_scene<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("scenes/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn get_capabilities(&self) -> Result<resource::Capabilities> {
parse_response(self.api_request("capabilities", RequestMethod::Get, None)?)
}
pub fn create_schedule(&self, creator: &resource::schedule::Creator) -> Result<String> {
creator.execute(&self)
}
pub fn set_schedule<S>(
&self,
id: S,
modifier: &resource::schedule::Modifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_schedule<S>(&self, id: S) -> Result<resource::Schedule>
where
S: Into<String>,
{
let id = id.into();
let schedule: resource::Schedule = parse_response(self.api_request(
format!("schedules/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(schedule.with_id(id))
}
pub fn get_all_schedules(&self) -> Result<Vec<resource::Schedule>> {
let map: HashMap<String, resource::Schedule> =
parse_response(self.api_request("schedules", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, schedule)| schedule.with_id(id))
.collect())
}
pub fn delete_schedule<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("schedules/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn create_resourcelink(&self, creator: &resource::resourcelink::Creator) -> Result<String> {
creator.execute(self)
}
pub fn set_resourcelink<S>(
&self,
id: S,
modifier: &resource::resourcelink::Modifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_resourcelink<S>(&self, id: S) -> Result<resource::Resourcelink>
where
S: Into<String>,
{
let id = id.into();
let resourcelink: resource::Resourcelink = parse_response(self.api_request(
format!("resourcelinks/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(resourcelink.with_id(id))
}
pub fn get_all_resourcelinks(&self) -> Result<Vec<resource::Resourcelink>> {
let map: HashMap<String, resource::Resourcelink> =
parse_response(self.api_request("resourcelinks", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, resourcelink)| resourcelink.with_id(id))
.collect())
}
pub fn delete_resourcelink<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("resourcelinks/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn set_sensor_attribute<S>(
&self,
id: S,
modifier: &resource::sensor::AttributeModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn set_sensor_state<S>(
&self,
id: S,
modifier: &resource::sensor::StateModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn set_sensor_config<S>(
&self,
id: S,
modifier: &resource::sensor::ConfigModifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_sensor<S>(&self, id: S) -> Result<resource::Sensor>
where
S: Into<String>,
{
let id = id.into();
let sensor: resource::Sensor = parse_response(self.api_request(
format!("sensors/{}", id),
RequestMethod::Get,
None,
)?)?;
Ok(sensor.with_id(id))
}
pub fn get_all_sensors(&self) -> Result<Vec<resource::Sensor>> {
let map: HashMap<String, resource::Sensor> =
parse_response(self.api_request("sensors", RequestMethod::Get, None)?)?;
Ok(map
.into_iter()
.map(|(id, sensor)| sensor.with_id(id))
.collect())
}
pub fn search_new_sensors(&self, scanner: &resource::sensor::Scanner) -> Result<()> {
scanner.execute(self)
}
pub fn get_new_sensors(&self) -> Result<resource::Scan> {
parse_response(self.api_request("senors/new", RequestMethod::Get, None)?)
}
pub fn delete_sensor<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> = self.api_request(
&format!("sensors/{}", id.into()),
RequestMethod::Delete,
None,
)?;
for i in response {
i.into_result()?;
}
Ok(())
}
pub fn create_rule(&self, creator: &resource::rule::Creator) -> Result<String> {
creator.execute(self)
}
pub fn set_rule<S>(
&self,
id: S,
modifier: &resource::rule::Modifier,
) -> Result<ResponsesModified>
where
S: Into<String>,
{
modifier.execute(self, id.into())
}
pub fn get_rule<S>(&self, id: S) -> Result<resource::Rule>
where
S: Into<String>,
{
let id = id.into();
let rule: resource::Rule =
parse_response(self.api_request(format!("rules/{}", id), RequestMethod::Get, None)?)?;
Ok(rule.with_id(id))
}
pub fn get_all_rules(&self) -> Result<Vec<resource::Rule>> {
let map: HashMap<String, resource::Rule> =
parse_response(self.api_request("rules", RequestMethod::Get, None)?)?;
Ok(map.into_iter().map(|(id, rule)| rule.with_id(id)).collect())
}
pub fn delete_rule<S>(&self, id: S) -> Result<()>
where
S: Into<String>,
{
let response: Vec<Response<JsonValue>> =
self.api_request(&format!("rules/{}", id.into()), RequestMethod::Delete, None)?;
for i in response {
i.into_result()?;
}
Ok(())
}
}