Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

odin_n5

The odin_n5 crate is an app domain crate to import fire sensor data from commercial N5ensors.com devices. Access to the sensor data requires a subscription from N5sensors.com, i.e. this crate can only be used with a valid app key from the vendor that is stored in a ODIN config file.

The data is retrieved via HTTP from the configured N5Sensors server. The API provides end points to query devices for basic information such as temperature and humidity and derived information such as smoke index and air quality. In addition, N5 sensors also supports an alert system that can be either explicitly queried (for selected devices) or used through a POST endpoint in the client system (e.g. an ODIN server). At this point odin_n5 only uses alert queries but the SpaService capability to define service specific routes is ideal to implement the POST notification channel.

This crate assumes that each application monitors a limited number of devices (<100). We do not yet provide an alarm server but plan to integrate the N5Actor into a heterogenous alert system that fuses different sensor types (e.g. odin_alertca webcams, opticalodin_sentinel sensors and various satellite sensors).

Design

This crate uses a common [’SpaServer](../odin_server/odin_server.mg) based design. Its major components are the N5Actorthat is responsible for data retrieval and client notification andN5Service` which specifies the assets and websocket handlers for this service.

                                                    odin │ external      
                                                         │               
ODIN/                  ┏━━━━━━━━━━━━━━━━━━━┓             │               
  config/              ┃ N5Actor           ┃             │               
    odin_n5/           ┃                   ┃             │  ┌───────────┐
      N5Config.ron  ───►  ┌─────────────────┐      http GET │ N5sensors │
                       ┃  │connector task ◄─┼──────────────►│  server   │
                       ┃  └─────────────────┘ JSON          │           │
                       ┃  ┌─────────────┐  ┃             │  └───────────┘
                       ┃  │N5DeviceStore│  ┃             │               
                       ┃  └─────────────┘  ┃             │               
                       ┃                   ┃                             
                       ┃   update_action   ┃                             
                       ┗━▲━━━━━━━━━━━━━━━━━┛                             
           exec_snapshot │      │                                        
         ┌───────────────┼──────┼──┐                                     
         │SpaServerActor │      │  │                                     
         │  ┏━━━━━━━━━━━━━━━━┓  │  │                                     
         │  ┃  N5Service     ┃  │  │                                     
         │  ┗━━━━━━━━━━━━▲━━━┛  │  │                                     
         └───────────────┼──────┼──┘                                     
                         │ wss  │               server                   
    ─────────────────────┼──────┼─────────────────────                   
                         │      │               client                   
                    ┌────┴──────▼───┐                                    
                    │  odin_n5.js   │                                    
                    └───────────────┘                                    

Since N5 devices do not use images or other data files the communication between the N5Actor and the N5sensors server only involves HTTP GET requests to which the N5Sensors server replies with JSON data. We only retrieve the last data points for each device on a configured interval. The amount of data is small enough to allow direct transmission to clients through their websockets. No cached data is needed.

Configuration

The odin_n5 crate only uses a single N5Config configuration struct that is primarily used to specify the URL for the external server together with the API key that is required to authenticate as a valid subscriber. This is also the reason why respective configuration files should not be part of the repository but kept outside within the ODIN server file system (e.g. ODIN_ROOT/configs/odin_n5/ - see odin_build)

config example for N5 access

N5Config(
    base_uri: "<N5-server-url>",
    api_key: "<your-secret-api-key>",

    max_history_len: 20, // keep 20 last data points per device
    data_cycles: 1, // we only get the most recent entry for each retrieve interval cycle
    retrieve_interval: Duration(secs: 300, nanos: 0), // retrieve device data every 5 minutes
    aggregate_interval: Duration(secs: 9000, nanos: 0), // average over 15min period (shortest duration)
)

Example

#![allow(unused)]
fn main() {
use odin_actor::prelude::*;
use odin_server::prelude::*;
use odin_n5::{
    actor::N5Actor, get_json_update_msg, get_n5_devices, live_connector::LiveN5Connector, 
    load_config, Device, N5Config, N5DataUpdate, N5DeviceStore, n5_service::N5Service,
};

run_actor_system!( actor_system => {
    let pre_n5 = PreActorHandle::new( &actor_system, "n5", 8);

    let hn5 = pre_n5.to_actor_handle();
    let hserver = spawn_actor!( actor_system, "server", 
        SpaServer::new(
            odin_server::load_config("spa_server.ron")?,
            "n5",
            SpaServiceList::new()
                .add( build_service!( => N5Service::new( hn5) ))
        )
    )?;

    let n5_id = pre_n5.get_id();
    let hn5 = spawn_pre_actor!( actor_system, pre_n5, 
        N5Actor::new( 
            LiveN5Connector::new( load_config("n5.ron")?),
            dataref_action!( 
                let sender_id: Arc<String> = n5_id, 
                let hserver: ActorHandle<SpaServerMsg> = hserver.clone() => |store: &N5DeviceStore| {
                    Ok( hserver.try_send_msg( DataAvailable::new::<N5DeviceStore>(sender_id) )? )
                }
            ),
            data_action!( 
                let hserver: ActorHandle<SpaServerMsg> = hserver.clone() => |updates: Vec<N5DataUpdate>| {
                    let ws_msg = get_json_update_msg( &updates);
                    Ok( hserver.try_send_msg( BroadcastWsMsg{ws_msg})? )
                }
            )
        )
    )?;

    Ok(())
});
}