Arete Control Plane for Smart Buildings
This example demonstrates use of the Arete SDK for smart buildings. It reproduces the Arete SDK example called "the switch and the light", by restructuring its flow as Lambda Trigger control loops, and Lambdas.
On a 1st node, the On Prem Agent runs "the switch", which consists of:
- a Lambda Trigger that subscribes to GPIO 04 edge events, and
- a Lambda that translates those events into a new desired state, and publishes it to the Arete control plane.
On a 2nd node, the On Prem Agent runs "the light", whcih consists of:
- a Lambda Trigger that subscribes to Arete control plane events containing desired state changes, and
- a Lambda that takes those events with new changes to desired state, and realizes it by setting a GPIO 23 pin.
Initial Provisioning
graph LR; cli --> control_plane; console --> control_plane; control_plane <-- tunnel --> agent_1; control_plane <-- tunnel --> agent_2; subgraph user_edge[User Edge] cli[CLI]; console[Console]; end subgraph cloud[Cloud <small>api.on-prem.net</small>] control_plane[Control Plane]; end subgraph device_edge_1[Device Edge] agent_1[Agent 1]; agent_2[Agent 2]; end
Subsequent Autonomous Edge Operation
graph TB; switch --> arete_control_plane; arete_control_plane --> light; subgraph device_edge[Device Edge] agent_1; agent_2; arete_control_plane; end subgraph agent_1[Agent 1] gpio_04_pin[GPIO 04 pin]; switch[Switch]; gpio_04_pin -- edge trigger --> switch; end subgraph agent_2[Agent 2] gpio_23_pin[GPIO 23 pin]; light[Light]; light --> gpio_23_pin; end arete_control_plane[(Arete Control Plane)];
Part 1: The Switch
Define the Lambda Trigger to detect the GPIO edge triggers
$ onprem generate xid
cj7ca3berad6gieb3rbg
# arete_switch_gpio_trigger.yaml
id: cj7ca3berad6gieb3rbg
kind: LambdaTrigger
name: arete_switch_gpio_trigger
description: >
Detect GPIO edge events, and emit them to a downstream lambda.
scriptContentType: Lua
script: >
local GPIO = require('periphery.GPIO')
local M = {}
function M.init(context)
-- Configure GPIO 04 pin for input
local params = {
path = '/dev/gpiochip0',
line = 4,
direction = 'in',
edge = 'both',
}
local pin = GPIO(params)
context['pin'] = pin
end
function M.run(context)
local pin = context.pin
while true do
local event = pin:read_event()
coroutine.yield(event)
end
end
return M
$ onprem apply ./arete_switch_gpio_trigger.yaml
Define the Lambda that will receive the GPIO events, and transmit to Arete
$ onprem generate xid
d322bqpcaq0mr0n3a2bg
# arete_switch.yaml
id: d322bqpcaq0mr0n3a2bg
kind: Lambda
name: arete_switch
description: >
Respond to GPIO edge events, and transmit as new desired state to the Arete control plane.
triggerId: cj7ca3berad6gieb3rbg
scriptContentType: Lua
script: >
local NODE_ID = 'ozr9fZbU8i7hMdjEjuTS2o'
local NODE_NAME = 'On Prem Arete Switch'
local CONTEXT_ID = 'uRLoYsXEY7nsbs9fRdjM8A'
local CONTEXT_NAME = 'Building 23, Office 41-B'
local PADI_LIGHT_PROFILE = 'padi.light'
local M = {}
function M.handler(event, context)
-- Configure Arete client, the first time
if context.areteClient == nil then
context['areteClient'] = M.newAreteClient()
end
local areteClient = context.areteClient
-- Transmit new desired state to Arete control plane
local desiredState = if event.edge == 'falling' then '1' else '0' end
areteClient:putProperty(NODE_ID, CONTEXT_ID, PADI_LIGHT_PROFILE, 'sOut', desiredState)
end
function M.newAreteClient()
local arete = require('arete-sdk')
local areteClient = arete.Client:new('wss://dashboard.test.cns.dev:443')
areteClient:waitForOpen()
areteClient:addSystem()
areteClient:addNode(NODE_ID, NODE_NAME)
areteClient:addContext(NODE_ID, CONTEXT_ID, CONTEXT_NAME)
areteClient:addProvider(NODE_ID, CONTEXT_ID, PADI_LIGHT_PROFILE)
return areteClient
end
return M
$ onprem apply ./arete_switch.yaml
Part 2: The Light
Define the Lambda Trigger that will detect changes to desired state
$ onprem generate xid
cj7ca3berad6gieb3rbg
# arete_light_trigger.yaml
id: cj7ca3berad6gieb3rbg
kind: LambdaTrigger
name: arete_light_trigger
description: >
Subscribe to the Arete control plane for the desired state of a light.
scriptContentType: Lua
script: >
local M = {}
function M.init(context)
context['areteClient'] = M.newAreteClient()
end
function M.run(context)
local areteClient = context.areteClient
for event, abort in areteClient:onUpdate() do
coroutine.yield(event)
end
end
function M.newAreteClient()
local arete = require('arete-sdk')
local NODE_ID = 'onqXVczGoymQkFc3UN6qcM'
local NODE_NAME = 'On Prem Arete Light'
local CONTEXT_ID = 'uRLoYsXEY7nsbs9fRdjM8A'
local CONTEXT_NAME = 'Building 23, Office 41-B'
local PADI_LIGHT_PROFILE = 'padi.light'
local areteClient = arete.Client:new('wss://dashboard.test.cns.dev:443')
areteClient:waitForOpen()
areteClient:addSystem()
areteClient:addNode(NODE_ID, NODE_NAME)
areteClient:addContext(NODE_ID, CONTEXT_ID, CONTEXT_NAME)
areteClient:addConsumer(NODE_ID, CONTEXT_ID, PADI_LIGHT_PROFILE)
return areteClient
end
return M
$ onprem apply ./arete_light_trigger.yaml
Define the Lambda that will realize the desired state by turning on a light
$ onprem generate xid
d322gs1caq0mvt65gb4g
# arete_light.yaml
id: d322gs1caq0mvt65gb4g
kind: Lambda
name: arete_light_trigger
description: >
Turn on a light.
triggerId: cj7ca3berad6gieb3rbg
scriptContentType: Lua
script: >
local GPIO = require('periphery.GPIO')
local M = {}
function M.handler(event, context)
-- Configure GPIO 23 pin for output, the first time
if context.pin == nil then
local params = {
path = '/dev/gpiochip0',
line = 23,
direction = 'out',
}
local pin = GPIO(params)
context['pin'] = pin
end
local pin = context.pin
-- Realize the desired state
local desiredState = event.sState == '1'
pin:write(desiredState)
end
return M
$ onprem apply ./arete_light.yaml