Posts: 264
Threads: 39
Joined: Feb 2016
Reputation:
1
IS it planned to develop Obix gateway like you did with Bacnet ?
What do you think about it.
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Posts: 429
Threads: 100
Joined: Jun 2015
Reputation:
45
by our information people are not using Obix too widely. You can realize same tasks on web services.
Maybe you can provide your arguments why it is worth to add support of Obix?
Thanks!
Posts: 264
Threads: 39
Joined: Feb 2016
Reputation:
1
no it was just a question, because I readed this:
http://www.knx.org/fileadmin/downloads/0...ebsite.pdf
And in this doc, Obix looks more open... It was just in order to have your though
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
(03.03.2016, 10:00)domotiqa Wrote: no it was just a question, because I readed this:
http://www.knx.org/fileadmin/downloads/0...ebsite.pdf
And in this doc, Obix looks more open... It was just in order to have your though
Hi Domotiqa,
Here are 2 functions i use to communicate with our SmartStruxure Lite controller with oBIX. Maybe you can use it.
-- 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
BR,
Erwin van der Zwart
Posts: 264
Threads: 39
Joined: Feb 2016
Reputation:
1
cool !!
Thank for the info
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
(05.03.2016, 14:00)domotiqa Wrote: cool !!
Thank for the info
You will need to add this: local mpm = require('socket.http') before using the mpm.request. The request is made to a basic auhtentication server.
I have a mpm.digest libary if you need digest login.
Have fun with it (;
BR,
Erwin van der Zwart
Posts: 429
Threads: 100
Joined: Jun 2015
Reputation:
45
good job, Erwin!
Posts: 429
Threads: 100
Joined: Jun 2015
Reputation:
45
thanks to Erwin van der Zwart, I am glad to post a complete oBIX module for integration with SmartStruxure Lite including Digest Authentication user library.
1. oBIX to MPM with digest - use as event or resident based script
Code: --======================================================================================================
--******************************** 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)
2. user.mpm_functions for oBIX to MPM with digest - user libary 1
Code: --======================================================================================================
--******************************* 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
3. user.mpm_digest for oBIX to MPM with digest - user libary 2
Code: --======================================================================================================
--******************************* 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,
}
|