Taxonomy, Adapters, Thinkerbell Project Link, March 16th, 2016
To the Things
To the front-end
The box
To the Things
Adapter
To the Things
Adapter The box To the Things
Adapter
To the Things
Adapter
REST/ WS
To the front-end
To the Things
Adapter
Adapter Adapter The box Manager To the Things
Adapter
To the Things
Adapter
Router
To the Things
REST/ WS
To the front-end
Let’s start with an example PUT /api/v1/channels { [{ "selector": { "tags": ["room: living room"], "kind": { "Temperature": [] }, }, "value": { "Temperature": { "C": 20.0 } } }] }
Let’s start with an example Send a batch of values
PUT /api/v1/channels to devices. { [{ "selector": { "tags": ["room: living room"], "kind": { "Temperature": [] }, }, "value": { "Temperature": { "C": 20.0 } } }] }
Let’s start with an example Send a batch of values
PUT /api/v1/channels to devices. { [{ "selector": { "tags": ["room: living room"], "kind": { "Temperature": [] }, Selector: All channels }, in the living room that "value": { support setting the temperature. "Temperature": { "C": 20.0 } } }] }
Let’s start with an example Send a batch of values
PUT /api/v1/channels to devices. { [{ "selector": { "tags": ["room: living room"], "kind": { "Temperature": [] }, Selector: All channels }, in the living room that "value": { support setting the temperature. "Temperature": { "C": 20.0 } } }] } A typed value.
Let’s start with an example let result = adapter.send_values(vec![ (vec![SetterSelector::new() .with_tags(vec![getter_tag!("room: living room")]) .with_kind(ChannelKind::Temperature) ], Temperature::C(20.0)) ]);
Let’s start with an example The Adapter Manager
let result = adapter.send_values(vec![ (vec![SetterSelector::new() .with_tags(vec![getter_tag!("room: living room")]) .with_kind(ChannelKind::Temperature) ], Temperature::C(20.0)) ]);
Let’s start with an example The Adapter Manager
Send a batch of values to devices.
let result = adapter.send_values(vec![ (vec![SetterSelector::new() .with_tags(vec![getter_tag!("room: living room")]) .with_kind(ChannelKind::Temperature) ], Temperature::C(20.0)) ]);
Let’s start with an example The Adapter Manager
Send a batch of values to devices.
let result = adapter.send_values(vec![ (vec![SetterSelector::new() .with_tags(vec![getter_tag!("room: living room")]) .with_kind(ChannelKind::Temperature) ], Temperature::C(20.0)) ]); Selector: All channels in the living room that support setting the temperature.
Let’s start with an example The Adapter Manager
Send a batch of values to devices.
let result = adapter.send_values(vec![ (vec![SetterSelector::new() .with_tags(vec![getter_tag!("room: living room")]) .with_kind(ChannelKind::Temperature) ], Temperature::C(20.0)) ]);
A typed value.
Selector: All channels in the living room that support setting the temperature.
The Taxonomy •
Crate foxbox_taxonomy.
•
Defines: Service, Channel, Selector.
•
Defines: Type, Value.
•
Provides: Native API behind REST/WS. •
Implemented by the Adapter Manager.
Service pub struct Service { /// Tags describing the service. /// /// These tags can be set by the user, adapters or /// applications. They are used by applications to find services and /// services. /// /// For instance, a user may set tag "entrance" to all services /// placed in the entrance of his house, or a tag "blue" to a service /// controlling blue lights. An adapter may set tags "plugged" or /// "battery" to devices that respectively depend on a plugged /// power source or on a battery. pub tags: HashSet
>, /// An id unique to this service. pub id: Id, /// Getter channels connected directly to this service. pub getters: HashMap, Channel>, /// Setter channels connected directly to this service. pub setters: HashMap, Channel>, /// Identifier of the adapter for this service. pub adapter: Id, }
~ Physical Device.
Service pub struct Service { /// Tags describing the service. /// /// These tags can be set by the user, adapters or /// applications. They are used by applications to find services and /// services. /// /// For instance, a user may set tag "entrance" to all services /// placed in the entrance of his house, or a tag "blue" to a service /// controlling blue lights. An adapter may set tags "plugged" or /// "battery" to devices that respectively depend on a plugged /// power source or on a battery. pub tags: HashSet>, /// An id unique to this service. pub id: Id, /// Getter channels connected directly to this service. pub getters: HashMap, Channel>, /// Setter channels connected directly to this service. pub setters: HashMap, Channel>, /// Identifier of the adapter for this service. pub adapter: Id, }
~ Physical Device.
Channel pub struct Channel where IO: IOMechanism { /// Tags describing the channel. /// /// These tags can be set by the user, adapters or /// applications. They are used to regroup channels for rules. /// /// For instance "entrance". pub tags: HashSet>, /// An id unique to this channel. pub id: Id, /// The service owning this channel. pub service: Id, /// The update mechanism for this channel. pub mechanism: IO, /// Identifier of the adapter for this channel. pub adapter: Id, /// The last time the device was seen. pub last_seen: Option, }
~ One thing a device can do.
Channel pub struct Channel where IO: IOMechanism { /// Tags describing the channel. /// /// These tags can be set by the user, adapters or /// applications. They are used to regroup channels for rules. /// /// For instance "entrance". pub tags: HashSet>, /// An id unique to this channel. pub id: Id, /// The service owning this channel. pub service: Id, /// The update mechanism for this channel. pub mechanism: IO, /// Identifier of the adapter for this channel. pub adapter: Id, /// The last time the device was seen. pub last_seen: Option, }
~ One thing a device can do.
Getter channels pub struct Getter { /// The kind of value that can be obtained from this channel. pub kind: ChannelKind, /// If `Some(duration)`, this channel can be polled, i.e. it /// will respond when the FoxBox requests the latest value. /// Parameter `duration` indicates the smallest interval /// between two updates. pub poll: Option, /// If `Some(duration)`, this channel can send the data to /// the FoxBox whenever it is updated. Parameter `duration` /// indicates the smallest interval between two updates. pub trigger: Option, /// If `true`, this channel supports watching for specific /// changes. pub watch: bool, /// Date at which the latest value was received, whether through /// polling or through a trigger. pub updated: Option, }
~ Something we can ask a channel.
Getter channels pub struct Getter { /// The kind of value that can be obtained from this channel. pub kind: ChannelKind, /// If `Some(duration)`, this channel can be polled, i.e. it /// will respond when the FoxBox requests the latest value. /// Parameter `duration` indicates the smallest interval /// between two updates. pub poll: Option, /// If `Some(duration)`, this channel can send the data to /// the FoxBox whenever it is updated. Parameter `duration` /// indicates the smallest interval between two updates. pub trigger: Option, /// If `true`, this channel supports watching for specific /// changes. pub watch: bool, /// Date at which the latest value was received, whether through /// polling or through a trigger. pub updated: Option, }
~ Something we can ask a channel.
Setter channels pub struct Setter { /// The kind of value that can be sent to this channel. pub kind: ChannelKind, /// If `Some(duration)`, this channel supports pushing, /// i.e. the FoxBox can send values. pub push: Option, /// Date at which the latest value was sent to the channel. pub updated: Option, }
~ Something we can tell a channel.
Setter channels pub struct Setter { /// The kind of value that can be sent to this channel. pub kind: ChannelKind, /// If `Some(duration)`, this channel supports pushing, /// i.e. the FoxBox can send values. pub push: Option, /// Date at which the latest value was sent to the channel. pub updated: Option, }
~ Something we can tell a channel.
The API /// A handle to the public API. pub trait API: Send { fn get_services(&self, Vec) -> Vec; fn get_getter_channels(&self, selectors: Vec) -> Vec>; fn get_setter_channels(&self, selectors: Vec) -> Vec>; fn add_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn fetch_values(&self, Vec) -> ResultMap, Option, Error>; fn send_values(&self, TargetMap) -> ResultMap, (), Error>; fn register_channel_watch(&self, watch: TargetMap>, on_event: Box>) -> Self::WatchGuard; /// A value that causes a disconnection once it is dropped. type WatchGuard; }
The API
Inspect topology
/// A handle to the public API. pub trait API: Send { fn get_services(&self, Vec) -> Vec; fn get_getter_channels(&self, selectors: Vec) -> Vec>; fn get_setter_channels(&self, selectors: Vec) -> Vec>; fn add_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn fetch_values(&self, Vec) -> ResultMap, Option, Error>; fn send_values(&self, TargetMap) -> ResultMap, (), Error>; fn register_channel_watch(&self, watch: TargetMap>, on_event: Box>) -> Self::WatchGuard; /// A value that causes a disconnection once it is dropped. type WatchGuard; }
The API
Inspect topology
/// A handle to the public API. pub trait API: Send { fn get_services(&self, Vec) -> Vec; fn get_getter_channels(&self, selectors: Vec) -> Vec>; fn get_setter_channels(&self, selectors: Vec) -> Vec>;
Manage tags
fn add_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn fetch_values(&self, Vec) -> ResultMap, Option, Error>; fn send_values(&self, TargetMap) -> ResultMap, (), Error>; fn register_channel_watch(&self, watch: TargetMap>, on_event: Box>) -> Self::WatchGuard; /// A value that causes a disconnection once it is dropped. type WatchGuard; }
The API
Inspect topology
/// A handle to the public API. pub trait API: Send { fn get_services(&self, Vec) -> Vec; fn get_getter_channels(&self, selectors: Vec) -> Vec>; fn get_setter_channels(&self, selectors: Vec) -> Vec>;
Manage tags
fn add_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn add_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_service_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_getter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn remove_setter_tags(&self, selectors: Vec, tags: Vec>) -> usize; fn fetch_values(&self, Vec) -> ResultMap, Option, Error>; fn send_values(&self, TargetMap) -> ResultMap, (), Error>; fn register_channel_watch(&self, watch: TargetMap>, on_event: Box>) -> Self::WatchGuard; /// A value that causes a disconnection once it is dropped. type WatchGuard; }
Fetch, send or watch.
Kinds, types, values pub enum ChannelKind { Ready, OnOff, OpenClosed, CurrentTime, CurrentTimeOfDay, RemainingTime, Thermostat, ActualTemperature, /// … Extension { vendor: String, adapter: String, kind: String, typ: Type } }
pub enum Type { Unit, OnOff, OpenClosed, Duration, TimeStamp, Temperature, String, Color, Json, Binary,
pub enum Value { Unit, OnOff(OnOff), OpenClosed(OpenClosed), Duration(ValDuration), TimeStamp(TimeStamp), Temperature(Temperature), Color(Color), String(Arc), // …
/// …
ExtBool(ExtBool), ExtNumeric(ExtNumeric), Json(Arc), Binary(Binary),
ExtBool, ExtNumeric, }
}
Kinds, types, values pub enum ChannelKind { Ready, OnOff, OpenClosed, CurrentTime, CurrentTimeOfDay, RemainingTime, Thermostat, ActualTemperature, /// … Extension { vendor: String, adapter: String, kind: String, typ: Type }
pub enum Type { Unit, OnOff, OpenClosed, Duration, TimeStamp, Temperature, String, Color, Json, Binary,
// …
/// …
ExtBool(ExtBool), ExtNumeric(ExtNumeric), Json(Arc), Binary(Binary),
ExtBool, ExtNumeric, }
}
Identifies actions.
pub enum Value { Unit, OnOff(OnOff), OpenClosed(OpenClosed), Duration(ValDuration), TimeStamp(TimeStamp), Temperature(Temperature), Color(Color), String(Arc),
Describes a format.
}
The actual value, with units if necessary.
Adapters API •
Create foxbox_adapters.
•
Lets code (un)register Adapters, Services, Channels.
•
Routes Send/Fetch/Watch request to Adapters.
•
Implemented by the Adapter Manager.
Provided for Adapters pub trait AdapterManagerHandle { fn add_adapter(&self, adapter: Box) -> Result<(), Error>; fn remove_adapter(&self, id: &Id) -> Result<(), Error>; fn add_service(&self, service: Service) -> Result<(), Error>; fn remove_service(&self, service_id: &Id) -> Result<(), Error>; fn add_getter(&self, setter: Channel) -> Result<(), Error>; fn remove_getter(&self, id: &Id) -> Result<(), Error>; fn add_setter(&self, setter: Channel) -> Result<(), Error>; fn remove_setter(&self, id: &Id) -> Result<(), Error>; }
Provided for Adapters pub trait AdapterManagerHandle { fn add_adapter(&self, adapter: Box) -> Result<(), Error>; fn remove_adapter(&self, id: &Id) -> Result<(), Error>; fn add_service(&self, service: Service) -> Result<(), Error>; fn remove_service(&self, service_id: &Id) -> Result<(), Error>; fn add_getter(&self, setter: Channel) -> Result<(), Error>; fn remove_getter(&self, id: &Id) -> Result<(), Error>; fn add_setter(&self, setter: Channel) -> Result<(), Error>; fn remove_setter(&self, id: &Id) -> Result<(), Error>; }
Adapters decide when to (un)register stuff.
Provided for Adapters pub trait AdapterManagerHandle { fn add_adapter(&self, adapter: Box) -> Result<(), Error>; fn remove_adapter(&self, id: &Id) -> Result<(), Error>; fn add_service(&self, service: Service) -> Result<(), Error>; fn remove_service(&self, service_id: &Id) -> Result<(), Error>; fn add_getter(&self, setter: Channel) -> Result<(), Error>; fn remove_getter(&self, id: &Id) -> Result<(), Error>; fn add_setter(&self, setter: Channel) -> Result<(), Error>; fn remove_setter(&self, id: &Id) -> Result<(), Error>; }
Adapters decide when to (un)register stuff. Id must persist across reboots.
Adapters must provide pub trait Adapter: Send { /// An id unique to this adapter. This id must persist between /// reboots/reconnections. fn id(&self) -> Id; /// The name of the adapter. fn name(&self) -> &str; fn vendor(&self) -> &str; fn version(&self) -> &[u32;4]; // ... more metadata fn fetch_values(&self, set: Vec>) -> ResultMap, Option, Error>; fn send_values(&self, values: Vec<(Id, Value)>) -> ResultMap, (), Error>; fn register_watch(&self, Vec<(Id, Option)>, cb: Box>) -> ResultMap, Box, Error>; }
Adapters must provide pub trait Adapter: Send { /// An id unique to this adapter. This id must persist between /// reboots/reconnections. fn id(&self) -> Id; /// The name of the adapter. fn name(&self) -> &str; fn vendor(&self) -> &str; fn version(&self) -> &[u32;4]; // ... more metadata fn fetch_values(&self, set: Vec>) -> ResultMap, Option, Error>; fn send_values(&self, values: Vec<(Id, Value)>) -> ResultMap, (), Error>; fn register_watch(&self, Vec<(Id, Option)>, cb: Box>) -> ResultMap, Box, Error>; }
Fetch, send or watch.
Thinkerbell aka IFTTT
Let’s start with an example •
Every day at 7pm, I would like to turn the heaters to 20ºC.
•
Every day at 9am, I would like to turn the heaters to 15ºC.
Let’s start with an example We should watch when a device with CurrentTimeOfDay becomes ≥7pm.
•
Every day at 7pm, I would like to turn the heaters to 20ºC.
•
Every day at 9am, I would like to turn the heaters to 15ºC.
Let’s start with an example We should watch when a device with CurrentTimeOfDay becomes ≥7pm.
•
•
Every day at 7pm, I would like to turn the heaters to 20ºC. We should send a message to all devices with Setter Temperature.
Every day at 9am, I would like to turn the heaters to 15ºC.
Scripts
•
When [selectors] reach state [range of values], do send [value] to [selectors].
Thinkerbell •
Provides: Script parsing, execution, monitoring.
•
Provides (WIP): CRUD API for scripts.
•
Provides (TBD): Rest API for the CRUD. •
Possibly as an Adapter :)
That’s all Folks!