oBIX gateway with LogicMachine
Example: oBIX gateway with LogicMachine
Task
The oBIX module below is created for LogicMachine integration with SmartStruxure Lite with Digest Authentication user library.
oBIX to MPM with digest – use as event or resident based script
- --======================================================================================================
- --******************************** MPM OBIX CONNECTION WITH DIGEST SUPPORT ***************************--
- --************************ Version 1.0 Created by Erwin van der Zwart 08-01-2016 *********************--
- --======================================================================================================
-
- require('user.mpm_functions') -- See this library for mpm number(s) with valid credentials and IP settings
-
- -- Select MPM (mpm number, autoresolve node id)
- mpm_settings = get_mpm_settings(1, true)
-
- -- get data from mpm (mpm settings, object, item)
- result = get_data_from_mpm(mpm_settings, "AV61", "Present_Value")
- log(result)
-
- -- post data to mpm (mpm number, object, item, value)
- post_data_to_mpm(mpm_settings, "AV61", "Present_Value", 111)
-
- -- get data from mpm (mpm settings, object, item)
- result = get_data_from_mpm(mpm_settings, "AV62", "Present_Value")
- log(result)
-
- -- post data to mpm (mpm number, object, item, value)
- post_data_to_mpm(mpm_settings, "AV62", "Present_Value", 222)
user.mpm_functions for oBIX to MPM with digest – user libary 1
- --======================================================================================================
- --******************************* MPM FUNCTION LIBRARY WITH DIGEST SUPPORT ***************************--
- --************************ Version 1.0 Created by Erwin van der Zwart 08-01-2016 *********************--
- --======================================================================================================
-
- local mpm = require "user.mpm_digest"
-
- function get_mpm_settings(mpm_number,autoresolve)
-
- function resolve_node()
- local url = "http://" .. mpm_username .. ":" .. mpm_password .. "@" .. mpm_ip
- local b, c, h = mpm.request(url)
- -- resolve node id automaticly (if not found we will use above but that one can be wrong)
- if h then
- mpm_node_id = string.sub(h.server, 1, 7)
- end
- return mpm_node_id
- end
-
- --======================================================================================================
- --**************************************** Put here your MPM settings ********************************--
- --======================================================================================================
-
- if mpm_number == 1 then -- Select MPM 1
- mpm_ip = '192.168.10.205'
- mpm_username = 'admin'
- mpm_password = 'Schneider'
- mpm_node_id = 'N004EAB'
- mpm_instance = 100
- elseif mpm_number == 2 then -- Select MPM 2
- mpm_ip = '192.168.10.206'
- mpm_username = 'admin'
- mpm_password = 'Schneider'
- mpm_node_id = 'N005EAB'
- mpm_instance = 150
- elseif mpm_number == 3 then -- Select MPM 3
- mpm_ip = '192.168.10.207'
- mpm_username = 'admin'
- mpm_password = 'Schneider'
- mpm_node_id = 'N006EAB'
- mpm_instance = 200
- else -- Select MPM with default settings (node ID is resolved automaticly)
- mpm_ip = '10.50.80.3'
- mpm_username = 'admin'
- mpm_password = 'admin'
- mpm_node_id = 'N000000'
- autoresolve = true
- mpm_instance = 100
- end
-
- --======================================================================================================
- --******************************************** End of MPM settings ***********************************--
- --======================================================================================================
-
-
- MPM_Settings = {ip = mpm_ip, username = mpm_username, password = mpm_password, node = mpm_node_id, instance = mpm_instance}
-
- if autoresolve then
- MPM_Settings.node = resolve_node()
- end
-
- return MPM_Settings
- end
-
- -- function to get data from mpm
- function get_data_from_mpm(mpm_settings,object,item)
- local url = "http://" .. mpm_settings.username .. ":" .. mpm_settings.password .. "@" .. mpm_settings.ip .. "/obix/network/" .. mpm_settings.node .. "/DEV" .. mpm_settings.instance .. "/" .. object .. "/" .. item
- local b, c, h = mpm.request(url)
- local value = b:match([[val="(.-)"]])
- local value = tonumber(value)
- return value
- end
-
- -- function to post data to mpm
- function post_data_to_mpm(mpm_settings,object,item,value)
- local url = "http://" .. mpm_settings.username .. ":" .. mpm_settings.password .. "@" .. mpm_settings.ip .. "/obix/network/" .. mpm_settings.node .. "/DEV" .. mpm_settings.instance .. "/" .. object .. "/" .. item .. "/"
- local reqBody = [[<intl val="]] .. value .. [["/>]]
- local headers = {["Content-Type"] = "text/xml", ["Content-Length"] = #reqBody}
- local respTable = {}
- local returnList = {client = {}, code = {}, headers = {}, status = {}}
- local source=ltn12.source.string(reqBody)
- local sink=ltn12.sink.table(respTable)
- local result = mpm.request{url=url, method="POST", source=source, sink=sink, headers=headers}
- return result
- end
user.mpm_digest for oBIX to MPM with digest – user libary 2
- --======================================================================================================
- --******************************* MPM DIGEST LIBRARY FOR DIGEST MD5 SUPPORT **************************--
- --************************ Version 1.0 Created by Erwin van der Zwart 08-01-2016 *********************--
- --======================================================================================================
-
- local md5sum = nil
-
- local md5 = {}
-
- local char, byte, format, rep, sub =
- string.char, string.byte, string.format, string.rep, string.sub
- local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
-
- local ok, bit = pcall(require, 'bit')
- if ok then
- bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
- else
- ok, bit = pcall(require, 'bit32')
-
- if ok then
-
- bit_not = bit.bnot
-
- local tobit = function(n)
- return n <= 0x7fffffff and n or -(bit_not(n) + 1)
- end
-
- local normalize = function(f)
- return function(a,b) return tobit(f(tobit(a), tobit(b))) end
- end
-
- bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
- bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
-
- else
-
- local function tbl2number(tbl)
- local result = 0
- local power = 1
- for i = 1, #tbl do
- result = result + tbl[i] * power
- power = power * 2
- end
- return result
- end
-
- local function expand(t1, t2)
- local big, small = t1, t2
- if(#big < #small) then
- big, small = small, big
- end
- for i = #small + 1, #big do
- small[i] = 0
- end
- end
-
- local to_bits
-
- bit_not = function(n)
- local tbl = to_bits(n)
- local size = math.max(#tbl, 32)
- for i = 1, size do
- if(tbl[i] == 1) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
- return tbl2number(tbl)
- end
-
- to_bits = function (n)
- if(n < 0) then
- return to_bits(bit_not(math.abs(n)) + 1)
- end
- local tbl = {}
- local cnt = 1
- local last
- while n > 0 do
- last = n % 2
- tbl[cnt] = last
- n = (n-last)/2
- cnt = cnt + 1
- end
-
- return tbl
- end
-
- bit_or = function(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
-
- local tbl = {}
- for i = 1, #tbl_m do
- if(tbl_m[i]== 0 and tbl_n[i] == 0) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
-
- return tbl2number(tbl)
- end
-
- bit_and = function(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
-
- local tbl = {}
- for i = 1, #tbl_m do
- if(tbl_m[i]== 0 or tbl_n[i] == 0) then
- tbl[i] = 0
- else
- tbl[i] = 1
- end
- end
-
- return tbl2number(tbl)
- end
-
- bit_xor = function(m, n)
- local tbl_m = to_bits(m)
- local tbl_n = to_bits(n)
- expand(tbl_m, tbl_n)
-
- local tbl = {}
- for i = 1, #tbl_m do
- if(tbl_m[i] ~= tbl_n[i]) then
- tbl[i] = 1
- else
- tbl[i] = 0
- end
- end
-
- return tbl2number(tbl)
- end
-
- bit_rshift = function(n, bits)
- local high_bit = 0
- if(n < 0) then
- n = bit_not(math.abs(n)) + 1
- high_bit = 0x80000000
- end
-
- local floor = math.floor
-
- for i=1, bits do
- n = n/2
- n = bit_or(floor(n), high_bit)
- end
- return floor(n)
- end
-
- bit_lshift = function(n, bits)
- if(n < 0) then
- n = bit_not(math.abs(n)) + 1
- end
-
- for i=1, bits do
- n = n*2
- end
- return bit_and(n, 0xFFFFFFFF)
- end
- end
- end
-
- local function lei2str(i)
- local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
- return f(0)..f(8)..f(16)..f(24)
- end
-
- local function str2bei(s)
- local v=0
- for i=1, #s do
- v = v * 256 + byte(s, i)
- end
- return v
- end
-
- local function str2lei(s)
- local v=0
- for i = #s,1,-1 do
- v = v*256 + byte(s, i)
- end
- return v
- end
-
- local function cut_le_str(s,...)
- local o, r = 1, {}
- local args = {...}
- for i=1, #args do
- table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
- o = o + args[i]
- end
- return r
- end
-
- local swap = function (w) return str2bei(lei2str(w)) end
-
- local function hex2binaryaux(hexval)
- return char(tonumber(hexval, 16))
- end
-
- local function hex2binary(hex)
- local result, _ = hex:gsub('..', hex2binaryaux)
- return result
- end
-
- local CONSTS = {
- 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
- 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
- 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
- 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
- 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
- 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
- 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
- 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
- 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
- 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
- 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
- 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
- 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
- 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
- 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
- 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
- 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
- }
-
- local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
- local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
- local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
- local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
- local z=function (f,a,b,c,d,x,s,ac)
- a=bit_and(a+f(b,c,d)+x+ac,0xFFFFFFFF)
- return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
- end
-
- local function transform(A,B,C,D,X)
- local a,b,c,d=A,B,C,D
- local t=CONSTS
-
- a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
- d=z(f,d,a,b,c,X[ 1],12,t[ 2])
- c=z(f,c,d,a,b,X[ 2],17,t[ 3])
- b=z(f,b,c,d,a,X[ 3],22,t[ 4])
- a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
- d=z(f,d,a,b,c,X[ 5],12,t[ 6])
- c=z(f,c,d,a,b,X[ 6],17,t[ 7])
- b=z(f,b,c,d,a,X[ 7],22,t[ 8])
- a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
- d=z(f,d,a,b,c,X[ 9],12,t[10])
- c=z(f,c,d,a,b,X[10],17,t[11])
- b=z(f,b,c,d,a,X[11],22,t[12])
- a=z(f,a,b,c,d,X[12], 7,t[13])
- d=z(f,d,a,b,c,X[13],12,t[14])
- c=z(f,c,d,a,b,X[14],17,t[15])
- b=z(f,b,c,d,a,X[15],22,t[16])
-
- a=z(g,a,b,c,d,X[ 1], 5,t[17])
- d=z(g,d,a,b,c,X[ 6], 9,t[18])
- c=z(g,c,d,a,b,X[11],14,t[19])
- b=z(g,b,c,d,a,X[ 0],20,t[20])
- a=z(g,a,b,c,d,X[ 5], 5,t[21])
- d=z(g,d,a,b,c,X[10], 9,t[22])
- c=z(g,c,d,a,b,X[15],14,t[23])
- b=z(g,b,c,d,a,X[ 4],20,t[24])
- a=z(g,a,b,c,d,X[ 9], 5,t[25])
- d=z(g,d,a,b,c,X[14], 9,t[26])
- c=z(g,c,d,a,b,X[ 3],14,t[27])
- b=z(g,b,c,d,a,X[ 8],20,t[28])
- a=z(g,a,b,c,d,X[13], 5,t[29])
- d=z(g,d,a,b,c,X[ 2], 9,t[30])
- c=z(g,c,d,a,b,X[ 7],14,t[31])
- b=z(g,b,c,d,a,X[12],20,t[32])
-
- a=z(h,a,b,c,d,X[ 5], 4,t[33])
- d=z(h,d,a,b,c,X[ 8],11,t[34])
- c=z(h,c,d,a,b,X[11],16,t[35])
- b=z(h,b,c,d,a,X[14],23,t[36])
- a=z(h,a,b,c,d,X[ 1], 4,t[37])
- d=z(h,d,a,b,c,X[ 4],11,t[38])
- c=z(h,c,d,a,b,X[ 7],16,t[39])
- b=z(h,b,c,d,a,X[10],23,t[40])
- a=z(h,a,b,c,d,X[13], 4,t[41])
- d=z(h,d,a,b,c,X[ 0],11,t[42])
- c=z(h,c,d,a,b,X[ 3],16,t[43])
- b=z(h,b,c,d,a,X[ 6],23,t[44])
- a=z(h,a,b,c,d,X[ 9], 4,t[45])
- d=z(h,d,a,b,c,X[12],11,t[46])
- c=z(h,c,d,a,b,X[15],16,t[47])
- b=z(h,b,c,d,a,X[ 2],23,t[48])
-
- a=z(i,a,b,c,d,X[ 0], 6,t[49])
- d=z(i,d,a,b,c,X[ 7],10,t[50])
- c=z(i,c,d,a,b,X[14],15,t[51])
- b=z(i,b,c,d,a,X[ 5],21,t[52])
- a=z(i,a,b,c,d,X[12], 6,t[53])
- d=z(i,d,a,b,c,X[ 3],10,t[54])
- c=z(i,c,d,a,b,X[10],15,t[55])
- b=z(i,b,c,d,a,X[ 1],21,t[56])
- a=z(i,a,b,c,d,X[ 8], 6,t[57])
- d=z(i,d,a,b,c,X[15],10,t[58])
- c=z(i,c,d,a,b,X[ 6],15,t[59])
- b=z(i,b,c,d,a,X[13],21,t[60])
- a=z(i,a,b,c,d,X[ 4], 6,t[61])
- d=z(i,d,a,b,c,X[11],10,t[62])
- c=z(i,c,d,a,b,X[ 2],15,t[63])
- b=z(i,b,c,d,a,X[ 9],21,t[64])
-
- return A+a,B+b,C+c,D+d
- end
-
- function md5.sumhexa(s)
- local msgLen = #s
- local padLen = 56 - msgLen % 64
-
- if msgLen % 64 > 56 then padLen = padLen + 64 end
-
- if padLen == 0 then padLen = 64 end
-
- s = s .. char(128) .. rep(char(0),padLen-1) .. lei2str(8*msgLen) .. lei2str(0)
-
- assert(#s % 64 == 0)
-
- local t = CONSTS
- local a,b,c,d = t[65],t[66],t[67],t[68]
-
- for i=1,#s,64 do
- local X = cut_le_str(sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
- assert(#X == 16)
- X[0] = table.remove(X,1)
- a,b,c,d = transform(a,b,c,d,X)
- end
-
- return format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
- end
-
- function md5.sum(s)
- return hex2binary(md5.sumhexa(s))
- end
-
- do -- select MD5 library
-
- local ok, mod = pcall(require, "crypto")
- if ok then
- local digest = (mod.evp or mod).digest
- if digest then
- md5sum = function(str) return digest("md5", str) end
- end
- end
-
- if not md5sum then
- local md5 = (type(mod) == "table") and mod or md5
- md5sum = md5.sumhexa or md5.digest
- end
-
- if not md5sum then
- ok = pcall(require, "digest") -- last because using globals
- if ok and md5 then md5sum = md5.digest end
- end
-
- end
-
- local s_http = require "socket.http"
- local s_url = require "socket.url"
- local ltn12 = require "ltn12"
-
- local hash = function(...)
- return md5sum(table.concat({...}, ":"))
- end
-
- local parse_header = function(h)
- local r = {}
- for k,v in (h .. ','):gmatch("(%w+)=(.-),") do
- if v:sub(1, 1) == '"' then -- strip quotes
- r[k:lower()] = v:sub(2, -2)
- else r[k:lower()] = v end
- end
- return r
- end
-
- local make_digest_header = function(t)
- local s = {}
- local x
- for i=1,#t do
- x = t[i]
- if x.unquote then
- s[i] = x[1] .. '=' .. x[2]
- else
- s[i] = x[1] .. '="' .. x[2] .. '"'
- end
- end
- return "Digest " .. table.concat(s, ', ')
- end
-
- local hcopy = function(t)
- local r = {}
- for k,v in pairs(t) do r[k] = v end
- return r
- end
-
- local _request = function(t)
- if not t.url then error("missing URL") end
- local url = s_url.parse(t.url)
- local user, password = url.user, url.password
- if not (user and password) then
- error("missing credentials in URL")
- end
- url.user, url.password, url.authority, url.userinfo = nil, nil, nil, nil
- t.url = s_url.build(url)
- local ghost_source
- if t.source then
- local ghost_chunks = {}
- local ghost_capture = function(x)
- if x then ghost_chunks[#ghost_chunks+1] = x end
- return x
- end
- local ghost_i = 0
- ghost_source = function()
- ghost_i = ghost_i+1
- return ghost_chunks[ghost_i]
- end
- t.source = ltn12.source.chain(t.source, ghost_capture)
- end
- local b, c, h = s_http.request(t)
- if (c == 401) and h["www-authenticate"] then
- local ht = parse_header(h["www-authenticate"])
- assert(ht.realm and ht.nonce and ht.opaque)
- if ht.qop ~= "auth" then
- return nil, string.format("unsupported qop (%s)", tostring(ht.qop))
- end
- if ht.algorithm and (ht.algorithm:lower() ~= "md5") then
- return nil, string.format("unsupported algo (%s)", tostring(ht.algorithm))
- end
- local nc, cnonce = "00000001", string.format("%08x", os.time())
- local uri = s_url.build{path = url.path, query = url.query}
- local method = t.method or "GET"
- local response = hash(
- hash(user, ht.realm, password),
- ht.nonce,
- nc,
- cnonce,
- "auth",
- hash(method, uri)
- )
- t.headers = t.headers or {}
- t.headers.authorization = make_digest_header{
- {"username", user},
- {"realm", ht.realm},
- {"nonce", ht.nonce},
- {"uri", uri},
- {"cnonce", cnonce},
- {"nc", nc, unquote=true},
- {"qop", "auth"},
- {"algorithm", "MD5"},
- {"response", response},
- {"opaque", ht.opaque},
- }
- if not t.headers.cookie and h["set-cookie"] then
- local cookie = (h["set-cookie"] .. ";"):match("(.-=.-)[;,]")
- if cookie then
- t.headers.cookie = "$Version: 0; " .. cookie .. ";"
- end
- end
- if t.source then t.source = ghost_source end
- b, c, h = s_http.request(t)
- return b, c, h
- else return b, c, h end
- end
-
- local request = function(x)
- local _t = type(x)
- if _t == "table" then
- return _request(hcopy(x))
- elseif _t == "string" then
- local r = {}
- local _, c, h = _request{url = x, sink = ltn12.sink.table(r)}
- return table.concat(r), c, h
- else error(string.format("unexpected type %s", _t)) end
- end
-
- return {
- request = request,
- }
Created by Erwin van der Zwart