Skip to content

Introduction of the LUA Edge Driver for ABB SCU200 InSite Energy Management System #2190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions drivers/SmartThings/abb-scu200/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: 'SCU200 InSite Energy Management System'
packageKey: 'ABB.SCU200'
description: "SmartThings driver for SCU200 InSite Energy Management System"
vendorSupportInformation: "https://support.smartthings.com"
permissions:
lan: {}
discovery: {}
34 changes: 34 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/auxiliary-contact.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: abb.scu200.auxiliary-contact.v1
components:
- id: main
capabilities:
- id: switch
version: 1
- id: refresh
version: 1
categories:
- name: Switch
deviceConfig:
dashboard:
states:
- component: main
capability: switch
version: 1
actions: []
detailView:
- component: main
capability: switch
version: 1
visibleCondition:
capability: switch
version: 1
component: main
value: switch.value
operator: ONE_OF
operand: '[""]'
automation:
conditions:
- component: main
capability: switch
version: 1
actions: []
8 changes: 8 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/bridge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: abb.scu200.bridge.v1
components:
- id: main
capabilities:
- id: refresh
version: 1
categories:
- name: Bridges
25 changes: 25 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/current-sensor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: abb.scu200.current-sensor.v1
components:
- id: main
capabilities:
- id: currentMeasurement
version: 1
- id: powerMeter
version: 1
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
- id: refresh
version: 1
categories:
- name: CurbPowerMeter
- id: productionMeter
label: "To Grid"
capabilities:
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
categories:
- name: CurbPowerMeter
36 changes: 36 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/energy-meter-module.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: abb.scu200.energy-meter-module.v1
components:
- id: main
capabilities:
- id: voltageMeasurement
version: 1
- id: currentMeasurement
version: 1
- id: powerMeter
version: 1
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
- id: refresh
version: 1
categories:
- name: CurbPowerMeter
- id: consumptionMeter
label: "From Grid"
capabilities:
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
categories:
- name: CurbPowerMeter
- id: productionMeter
label: "To Grid"
capabilities:
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
categories:
- name: CurbPowerMeter
27 changes: 27 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/energy-meter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: abb.scu200.energy-meter.v1
components:
- id: main
capabilities:
- id: voltageMeasurement
version: 1
- id: currentMeasurement
version: 1
- id: powerMeter
version: 1
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
- id: refresh
version: 1
categories:
- name: CurbPowerMeter
- id: productionMeter
label: "To Grid"
capabilities:
- id: powerConsumptionReport
version: 1
- id: energyMeter
version: 1
categories:
- name: CurbPowerMeter
10 changes: 10 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/gas-meter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: abb.scu200.gas-meter.v1
components:
- id: main
capabilities:
- id: gasMeter
version: 1
- id: refresh
version: 1
categories:
- name: Others
10 changes: 10 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/output-module.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: abb.scu200.output-module.v1
components:
- id: main
capabilities:
- id: switch
version: 1
- id: refresh
version: 1
categories:
- name: Switch
14 changes: 14 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/usb-energy-meter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: abb.scu200.usb-energy-meter.v1
components:
- id: main
capabilities:
- id: powerMeter
version: 1
- id: energyMeter
version: 1
- id: powerConsumptionReport
version: 1
- id: refresh
version: 1
categories:
- name: CurbPowerMeter
10 changes: 10 additions & 0 deletions drivers/SmartThings/abb-scu200/profiles/water-meter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: abb.scu200.water-meter.v1
components:
- id: main
capabilities:
- id: waterMeter
version: 1
- id: refresh
version: 1
categories:
- name: Others
2 changes: 2 additions & 0 deletions drivers/SmartThings/abb-scu200/search-parameters.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ssdp:
- searchTerm: urn:ABB:device:SCU200:1
145 changes: 145 additions & 0 deletions drivers/SmartThings/abb-scu200/src/abb/api.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
local log = require("log")
local st_utils = require "st.utils"
local json = require "st.json"

-- Local imports
local config = require("config")
local utils = require("utils")
local RestClient = require "lunchbox.rest"

-- API for the ABB SCU200 Bridge
local api = {}
api.__index = api

local SSL_CONFIG = {
mode = "client",
protocol = "any",
verify = "none",
options = "all"
}

local ADDITIONAL_HEADERS = {
["Accept"] = "application/json",
["Content-Type"] = "application/json",
}

-- Method for getting the base URL
local function get_base_url(bridge_ip)
return "https://" .. bridge_ip .. ":" .. config.REST_API_PORT
end

-- Method for processing the REST response
local function process_rest_response(response, err, partial)
if err ~= nil then
return response, err, nil
elseif response ~= nil then
local status, decoded_json = pcall(json.decode, response:get_body())

if status and response.status == 200 then
log.debug("process_rest_response(): Response = " .. response.status .. " " .. response:get_body())

return decoded_json, nil, response.status
elseif status then
log.error("process_rest_response(): Response error = " .. response.status)

return nil, "response status is not 200 OK", response.status
else
log.error("process_rest_response(): Failed to decode data")

return nil, "failed to decode data", nil
end
else
return nil, "no response or error received", nil
end
end

-- Method for creating a retry function
local function retry_fn(retry_attempts)
local count = 0

return function()
count = count + 1
return count < retry_attempts
end
end

-- Method for performing a GET request
local function do_get(api_instance, path)
log.debug("do_get(): Sending GET request to " .. path)

return process_rest_response(api_instance.client:get(path, api_instance.headers, retry_fn(5)))
end

-- Method for performing a POST request
local function do_post(api_instance, path, payload)
log.debug("do_post(): Sending POST request to " .. path .. " with payload " .. json.encode(payload))

return process_rest_response(api_instance.client:post(path, payload, api_instance.headers, retry_fn(5)))
end

-- Method for creating a labeled socket builder
function api.labeled_socket_builder(label)
local socket_builder = utils.labeled_socket_builder(label, SSL_CONFIG)

return socket_builder
end

-- Method for creating a new bridge manager
function api.new_bridge_manager(bridge_ip, bridge_dni)
local base_url = get_base_url(bridge_ip)
local socket_builder = api.labeled_socket_builder(bridge_dni)

return setmetatable(
{
headers = st_utils.deep_copy(ADDITIONAL_HEADERS),
client = RestClient.new(base_url, socket_builder),
base_url = base_url
},
api
)
end

-- Method for getting the thing infos
function api.get_thing_infos(bridge_ip, bridge_dni)
local socket_builder = api.labeled_socket_builder(bridge_dni .. " (thing infos)")
local response, error, status = process_rest_response(RestClient.one_shot_get(get_base_url(bridge_ip) .. "/devices", ADDITIONAL_HEADERS, socket_builder))

if not error and status == 200 then
return response
else
log.error("api.get_thing_infos(): Failed to get thing infos, error = " .. error)
return nil
end
end

-- Method for getting the bridge info
function api.get_bridge_info(bridge_ip, bridge_dni)
local socket_builder = api.labeled_socket_builder(bridge_dni .. " (bridge info)")
local response, error, status = process_rest_response(RestClient.one_shot_get(get_base_url(bridge_ip) .. "/bridge", ADDITIONAL_HEADERS, socket_builder))

if not error and status == 200 then
return response
else
log.error("api.get_bridge_info(): Failed to get thing infos, error = " .. error)
return nil
end
end

-- API methods
function api:get_devices()
return do_get(self, "/devices")
end

function api:get_device_by_id(id)
return do_get(self, string.format("/devices/%s", id))
end

function api:post_device_by_id(id, payload)
return do_post(self, string.format("/devices/%s/control", id), payload)
end

function api:get_sse_url()
return self.base_url .. "/events"
end

return api
Loading