Module Integration Guides
Linux
Integrate Config Updates
5 min
bytebeam enables you to remotely change the configuration of your application this is done by sending json based configuration to the device this guide will walk you through how to achieve this creating new device config steps to update configuration step 1 go to the device management section in bytebeam cloud step 2 go to the "device configs" section step 3 click on "new device config" step 4 enter the appropriate details for the device configuration in this example, we are changing the current log level of our python application the new device config should be visible now on the device, first, register this action with uplink using its configuration file config toml as we did in the receiving actions section this file is present in /usr/local/share/bytebeam folder edit this file to add the line actions=\[{name="update config"}] under \[tcpapps 1] section config toml persistence path = "/tmp/uplink" action redirections={update firmware="install firmware"} \[tcpapps 1] port=5050 actions=\[{name="reboot"}, {name="update config"}] \[downloader] path="/tmp/uplink/download" actions=\[{name="update firmware", timeout=610}, {name="send file"}] \[apis] enabled=true port=3333 \[ota installer] path="/tmp/uplink/installer" actions=\[{name="install firmware", timeout=610}] uplink port=5050 \[logging] tags=\["sshd", "systemd"] stream size=1 min level=7 restart the uplink using the following commands sudo systemctl restart uplink next using the following example you can update the configuration on your device import socket import json import time import threading import logging s = socket socket(socket af inet, socket sock stream) s connect(("localhost", 5050)) \# converts json data received over tcp into a python dictionary def receive action(s) return json loads(s recv(2048)) \# constructs a payload and sends it over tcp to uplink def send data(s, payload) send = json dumps(payload) + "\n" s sendall(bytes(send, encoding="utf 8")) \# constructs a json `action status` as a response to received action on completion def action complete(id) return { "stream" "action status", "sequence" 0, "timestamp" int(time time() 1000000), "action id" id, "state" "completed", "progress" 100, "errors" \[], } def update config(action) payload = json loads(action\["payload"]) print(payload) app = payload\["name"] print(app) level = payload\["level"] if app == "logging" set log level(level) resp = action complete(action\["action id"]) print(resp) send data(s, resp) def receive actions() while true action = receive action(s) print(action) if action\["name"] == "update config" print("update config action received") print(json loads(action\["payload"])) update config(action) def set log level(level) if level lower() == "info" logging getlogger() setlevel(logging info) elif level lower() == "error" logging getlogger() setlevel(logging error) elif level lower() == "verbose" logging getlogger() setlevel(logging verbose) elif level lower() == "debug" logging getlogger() setlevel(logging debug) print("starting uplink bridge app") threading thread(target=receive actions) start() logging getlogger() setlevel(logging info) logging formatter("%(levelname)s %(message)s") while true time sleep(5) logging debug("this is debug") logging info("this is info") javascript const logger = log4js getlogger(); const net = require("net"); const log4js = require("log4js"); const client = new net socket(); client connect(5050, "localhost", () => { console log("connected to server"); }); // configure logger log4js configure({ appenders { console { type "console" } }, categories { default { appenders \["console"], level "info" } }, }); // receive json data over tcp function receiveaction(socket) { return new promise((resolve, reject) => { socket once("data", (data) => { try { const action = json parse(data tostring()); resolve(action); } catch (error) { reject(error); } }); }); } // send json payload over tcp function senddata(socket, payload) { const send = json stringify(payload) + "\n"; socket write(send); } // construct and send action status function actioncomplete(id) { return { stream "action status", sequence 0, timestamp date now(), action id id, state "completed", progress 100, errors \[], }; } async function updateconfig(action) { const payload = json parse(action payload); console log(payload); const app = payload name; console log(app); const level = payload level; if (app === "logging") { setloglevel(level); } const resp = actioncomplete(action action id); console log(resp); senddata(client, resp); } // receiving actions async function receiveactions() { while (true) { const action = await receiveaction(client); console log(action); if (action name === "update config") { console log("update config action received"); console log(json parse(action payload)); await updateconfig(action); } } } console log("starting uplink bridge app"); receiveactions(); // log messages at intervals setinterval(() => { logger debug("this is debug"); logger info("this is info"); }, 5000); rust use chrono utc; use flexi logger {levelfilter, logspecification, logger}; use log {debug, error, info}; use serde json json; use std io {bufread, bufreader, write}; use std net tcpstream; use std thread; fn receive action(stream \&mut tcpstream) > serde json result\<serde json value> { let mut reader = bufreader new(stream); let mut buffer = string new(); reader read line(\&mut buffer)?; serde json from str(\&buffer) } fn send data(stream \&mut tcpstream, payload serde json value) { let serialized = serde json to string(\&payload) unwrap() + "\n"; stream write all(serialized as bytes()) unwrap(); } fn action complete(action id \&str) > serde json value { json!({ "stream" "action status", "sequence" 0, "timestamp" utc now() timestamp millis(), "action id" action id, "state" "completed", "progress" 100, "errors" \[] }) } fn update config(stream \&mut tcpstream, action \&serde json value) { let payload serde json value = serde json from str(action\["payload"] as str() unwrap()) unwrap(); let app = payload\["name"] as str() unwrap(); let level = payload\["level"] as str() unwrap(); if app == "logging" { set log level(level); } let resp = action complete(action\["action id"] as str() unwrap()); send data(stream, resp); } fn set log level(level \&str) { let level filter = match level to lowercase() as str() { "info" => levelfilter info, "error" => levelfilter error, "verbose" => levelfilter trace, // rust doesn't have 'verbose', using 'trace' instead "debug" => levelfilter debug, => levelfilter info, }; let log spec = logspecification default() with default(level filter); logger with(log spec) start() unwrap or else(|e| { eprintln!("failed to initialize logger {}", e); }); info!("log level set to {}", level); } fn receive actions(mut stream tcpstream) { loop { let action = receive action(\&mut stream) unwrap(); if action\["name"] == "update config" { update config(\&mut stream, \&action); } } } fn main() { logger with env or str("info") start() unwrap(); let mut stream = tcpstream connect("localhost 5050") unwrap(); println!("starting uplink bridge app"); let stream clone = stream try clone() unwrap(); thread spawn(move || { receive actions(stream clone); }); loop { thread sleep(std time duration from secs(5)); debug!("this is debug"); info!("this is info"); error!("this is error"); } } go package main import ( "bufio" "encoding/json" "fmt" "io" "net" "os" "time" log "github com/sirupsen/logrus" ) func main() { conn, err = net dial("tcp", "localhost 5050") if err != nil { fmt println("error connecting ", err) os exit(1) } defer conn close() log setlevel(log infolevel) log setformatter(\&log textformatter{}) log setoutput(os stdout) go receiveactions(conn) fmt println("starting uplink bridge app") for { time sleep(5 time second) log debug("this is debug") log info("this is info") log error("this is error") } } // receiveaction reads and decodes a json message from the connection func receiveaction(conn net conn) (map\[string]interface{}, error) { reader = bufio newreader(conn) msg, err = reader readstring('\n') if err != nil { return nil, err } var action map\[string]interface{} err = json unmarshal(\[]byte(msg), \&action) return action, err } // sendaction encodes a payload as json and sends it over the connection func sendaction(conn net conn, payload map\[string]interface{}) error { msg, err = json marshal(payload) if err != nil { return err } , err = conn write(append(msg, '\n')) return err } // actioncomplete constructs a json `action status` as a response to received action on completion func actioncomplete(id interface{}) map\[string]interface{} { return map\[string]interface{}{ "stream" "action status", "sequence" 0, "timestamp" time now() unixnano() / 1000, "action id" id, "state" "completed", "progress" 100, "errors" \[]string{}, } } // updateconfig processes the update config action func updateconfig(conn net conn, action map\[string]interface{}) { payloadstr = action\["payload"] (string) var payload map\[string]interface{} json unmarshal(\[]byte(payloadstr), \&payload) app = payload\["name"] (string) level = payload\["level"] (string) if app == "logging" { setloglevel(level) } resp = actioncomplete(action\["action id"]) sendaction(conn, resp) } func setloglevel(level string) { switch level { case "info" log setlevel(log infolevel) case "error" log setlevel(log errorlevel) case "verbose" log setlevel(log tracelevel) // go's logrus doesn't have 'verbose', using 'trace' instead case "debug" log setlevel(log debuglevel) default log setlevel(log infolevel) } log infof("log level set to %s", level) } // recvactions handles incoming actions in a separate goroutine func receiveactions(conn net conn) { for { action, err = receiveaction(conn) if err != nil { if err == io eof { fmt println("connection closed by server") return } fmt println("error reading ", err) continue } fmt println("action received ", action) if action\["name"] == "update config" { fmt println("update config action received") updateconfig(conn, action) } } } triggering config update on the device management section of bytebeam cloud, select the device and click on update configuration select the appropriate config version confirm the selection you should now be able to see the status of the update on the cloud