Da ich noch einige DECT200 Aktoren mein eigen nenne, wollte ich diese gern über mein HC2 schalten. Meine Anwesenheitssteuerung erledige ich über ein Script aus diesem Forum, welches mit meinen iPhones sehr gut funktioniert (https://www.siio.de/board/thema/tr-064-in-lua-script-ansprechen/) Dieses habe ich etwas verändert und kann damit meine DECT200 Aktoren sicher schalten. Das geänderte Script stelle ich hier mit in diesen Beitrag. Es ist eine Lua Scene zu erstellen mit manueller Ausführung, die Scene wird dann über das VD gestartet. Folgende 3 Zeilen müssen Angepasst werden.
Zeile 15 IP Adresse der Fritzbox
Zeile 16 Benutzer der FritzBox
Zeile 17Passwort der Fritzbox
local URL="192.168.1.1" -- Fritzbox IP / Hostname
local USERNAME="Benutzer" -- Username of "Smarthome"-User in Fritzbox
local PASSWORD="Passwort" -- Passwort of "Smarthome"-User in Fritzbox
Hier kommt der Rest des Scripts, es ist sehr lang da für jede Anfrage eine Session ID erstellt werden muss. Nach dem Script erkläre ich die Erstellung des eigentlichen VD.
--[[
%% properties
%% events
%% globals
--]]
-- Abort second instance
if (fibaro:countScenes() > 1) then
fibaro:abort();
end
----------------------------------------------------------------
-- These variables must be set
local URL="192.168.1.1" -- Fritzbox IP / Hostname
local USERNAME="bulli" -- Username of "Smarthome"-User in Fritzbox
local PASSWORD="blumB1" -- Passwort of "Smarthome"-User in Fritzbox
local SHOW_DEBUG = true; -- Show debugmessages true/false
local SHOW_INFO = true; -- Show infomessages true/false
local akt = fibaro:getGlobal("aktor")
local azu = fibaro:getGlobal("azustand")
-- Scroll down to the bottom to activate the individual checks.
--------------------------------------------------------------
------------------ Debug Output ------------------------------
local function ShowMessage ( typ, color, message )
if((typ == 'debug' and SHOW_DEBUG) or (typ == 'info' and SHOW_INFO)) then
fibaro:debug(string.format('<%s style="color:%s;">%s', "span", color, message, "span"))
end
end
------------------ Check Presence -----------------------------
local function checkPresence ( fritzWebData, mac, name, state_variable )
local fritzWebDataWork = fritzWebData;
local found = nil;
local variable = nil;
fritzWebDataWork = fritzWebData;
fritzWebDataWork = string.gsub(fritzWebDataWork, "</?[A-Za-z]+[^>]*>", "");
fritzWebDataWork = string.gsub(fritzWebDataWork, "WLAN", "");
fritzWebDataWork = string.sub(fritzWebDataWork,string.find(fritzWebDataWork,'Ungenutzte Verbindungen'),-1);
fritzWebDataWork = string.sub(fritzWebDataWork,24,string.find(fritzWebDataWork,'Hier können')-1);
found = string.match(fritzWebDataWork, mac);
ShowMessage('info', 'magenta', 'Check '..mac);
variable = nil;
if found == nil and fritzWebData ~= nil then
variable = 1;
fibaro:setGlobal(state_variable, "1");
ShowMessage('info', 'green', name..': '..variable);
else
variable = 0;
fibaro:setGlobal(state_variable, "0");
ShowMessage('info', 'red', name..': '..variable);
end;
end
------------------ BIT Library -------------------------------
local function check_int(n)
-- checking not float
if(n - math.floor(n) > 0) then
error("trying to use bitwise operation on non-integer!")
end
end
local function tbl_to_number(tbl)
local n = #tbl
local rslt = 0
local power = 1
for i = 1, n do
rslt = rslt + tbl[i]*power
power = power*2
end
return rslt
end
local function expand(tbl_m, tbl_n)
local big = {}
local small = {}
if(#tbl_m > #tbl_n) then
big = tbl_m
small = tbl_n
else
big = tbl_n
small = tbl_m
end
-- expand small
for i = #small + 1, #big do
small[i] = 0
end
end
local to_bits = function () end
local function bit_not(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 tbl_to_number(tbl)
end
to_bits = function (n)
check_int(n)
if(n < 0) then
-- negative
return to_bits(bit_not(math.abs(n)) + 1)
end
-- to bits table
local tbl = {}
local cnt = 1
while (n > 0) do
local last = n%2
if(last == 1) then
tbl[cnt] = 1
else
tbl[cnt] = 0
end
n = (n-last)/2
cnt = cnt + 1
end
return tbl
end
local function bit_or(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(#tbl_m, #tbl_n)
for i = 1, rslt do
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl_to_number(tbl)
end
local function bit_and(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(#tbl_m, #tbl_n)
for i = 1, rslt do
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl_to_number(tbl)
end
local function bit_xor(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(#tbl_m, #tbl_n)
for i = 1, rslt do
if(tbl_m[i] ~= tbl_n[i]) then
tbl[i] = 1
else
tbl[i] = 0
end
end
--table.foreach(tbl, print)
return tbl_to_number(tbl)
end
local function bit_rshift(n, bits)
check_int(n)
local high_bit = 0
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
high_bit = 2147483648 -- 0x80000000
end
for i=1, bits do
n = n/2
n = bit_or(math.floor(n), high_bit)
end
return math.floor(n)
end
-- logic rightshift assures zero filling shift
local function bit_logic_rshift(n, bits)
check_int(n)
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
end
for i=1, bits do
n = n/2
end
return math.floor(n)
end
local function bit_lshift(n, bits)
check_int(n)
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
end
for i=1, bits do
n = n*2
end
return bit_and(n, 4294967295) -- 0xFFFFFFFF
end
local function bit_xor2(m, n)
local rhs = bit_or(bit_not(m), bit_not(n))
local lhs = bit_or(m, n)
local rslt = bit_and(lhs, rhs)
return rslt
end
-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
-- 10/02/2001 jcw@equi4.com
local md5={ff=tonumber('ffffffff',16),consts={}}
string.gsub([[ d76aa478 e8c7b756 242070db c1bdceee
f57c0faf 4787c62a a8304613 fd469501
698098d8 8b44f7af ffff5bb1 895cd7be
6b901122 fd987193 a679438e 49b40821
f61e2562 c040b340 265e5a51 e9b6c7aa
d62f105d 02441453 d8a1e681 e7d3fbc8
21e1cde6 c33707d6 f4d50d87 455a14ed
a9e3e905 fcefa3f8 676f02d9 8d2a4c8a
fffa3942 8771f681 6d9d6122 fde5380c
a4beea44 4bdecfa9 f6bb4b60 bebfbc70
289b7ec6 eaa127fa d4ef3085 04881d05
d9d4d039 e6db99e5 1fa27cf8 c4ac5665
f4292244 432aff97 ab9423a7 fc93a039
655b59c3 8f0ccc92 ffeff47d 85845dd1
6fa87e4f fe2ce6e0 a3014314 4e0811a1
f7537e82 bd3af235 2ad7d2bb eb86d391
67452301 efcdab89 98badcfe 10325476 ]],"(%w+)", function (s) table.insert(md5.consts, tonumber(s,16)) end)
--67452301 efcdab89 98badcfe 10325476 ]],"(%w+)", function (s) tinsert(md5.consts,tonumber(s,16)) end)
function md5.transform(A,B,C,D,X)
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,md5.ff)
-- be *very* careful that left shift does not cause rounding!
return bit_or(bit_lshift(bit_and(a,bit_rshift(md5.ff,s)),s),bit_rshift(a,32-s))+b
end
local a,b,c,d=A,B,C,D
local t=md5.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
-- convert little-endian 32-bit int to a 4-char string
local function leIstr(i)
local f=function (s) return string.char(bit_and(bit_rshift(i,s),255)) end
return f(0)..f(8)..f(16)..f(24)
end
-- convert raw string to big-endian int
local function beInt(s)
local v=0
for i=1,string.len(s) do v=v*256+string.byte(s,i) end
return v
end
-- convert raw string to little-endian int
local function leInt(s)
local v=0
for i=string.len(s),1,-1 do v=v*256+string.byte(s,i) end
return v
end
-- cut up a string in little-endian ints of given size
local function leStrCuts(s)
local o,r=1,{}
for i=1,16 do
table.insert(r,leInt(string.sub(s,o,o+4-1)))
o=o+4
end
return r
end
function md5.Calc(s)
local msgLen=string.len(s)
local padLen=56- msgLen % 64
if msgLen % 64 > 56 then padLen=padLen+64 end
if padLen==0 then padLen=64 end
s=s..string.char(128)..string.rep(string.char(0),padLen-1)
s=s..leIstr(8*msgLen)..leIstr(0)
assert(string.len(s) % 64 ==0)
local t=md5.consts
local a,b,c,d=t[65],t[66],t[67],t[68]
for i=1,string.len(s),64 do
local X=leStrCuts(string.sub(s,i,i+63))
assert(#X==16)
X[0]=table.remove(X,1) -- zero based!
a,b,c,d=md5.transform(a,b,c,d,X)
end
local swap=function (w) return beInt(leIstr(w)) end
return string.format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
end
------------ Library for encoding UTF8 to UTF16LE
local LE = 0x1
local DE = 0x2
local BYTE_1_HEAD = 0x00 -- 0000 0000
local BYTE_2_HEAD = 0xC0 -- 1100 0000
local BYTE_3_HEAD = 0xE0 -- 1110 0000
local BYTE_TAIL_HEAD = 0x80 -- 1000 0000
local BYTE_1_MASK = 0x80 -- 1000 0000
local BYTE_2_MASK = 0xE0 -- 1110 0000
local BYTE_3_MASK = 0xF0 -- 1111 0000
local BYTE_TAIL_MASK = 0x3F -- 0011 1111
local Format_char = function(b)
return string.format("%c", b)
end
function UTF8To16(utf8, order)
assert(type(utf8) == 'string')
local result, tmp = {}, {}
local i, b1, b2, b3, high, low = 1, 0, 0, 0, 0, 0
local len = #utf8
while i <= len do
b1 = string.byte(utf8, i)
if bit_and(b1, BYTE_1_MASK) == BYTE_1_HEAD then -- 0### ####
low = Format_char(b1); high = "\0"; i = i + 1
elseif bit_and(b1, BYTE_2_MASK) == BYTE_2_HEAD then -- 110# ####
b2 = string.byte(utf8, i + 1)
high = Format_char(bit_rshift(bit_and(bit_not(BYTE_2_MASK), b1), 2))
low = Format_char(bit_or(bit_and(BYTE_TAIL_MASK, b2), bit_lshift(b1, 6)))
i = i + 2
elseif bit_and(b1, BYTE_3_MASK) == BYTE_3_HEAD then -- 1110 ####
b2, b3 = string.byte(utf8, i + 1, i + 2)
high = Format_char(bit_or(bit_lshift(b1, 4), bit_rshift(bit_and(BYTE_TAIL_MASK, b2), 2)))
low = Format_char(bit_or(bit_lshift(b2, 6), bit_and(BYTE_TAIL_MASK, b3)))
i = i + 3
end
if order == DE then low, high = high, low end
table.insert(result, low .. high)
end
return table.concat(result)
end
------------------------ Web Requests ------------------------
local function getMethod(requestUrl, successCallback, errorCallback)
local http = net.HTTPClient()
http:request(requestUrl, {
options = {
method = 'GET'
},
success = successCallback,
error = errorCallback
})
end
local function postMethod(requestUrl, data, successCallback, errorCallback)
local http = net.HTTPClient()
http:request(requestUrl, {
options = {
headers = {["content-Type"] = "application/x-www-form-urlencoded"},
method = 'POST',
redirect = false,
data = data,
timeout = 5000
},
success = successCallback,
error = errorCallback
})
end
function tprint (tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
print(formatting)
tprint(v, indent+1)
elseif type(v) == 'boolean' then
print(formatting .. tostring(v))
else
print(formatting .. v)
end
end
end
getMethod("http://"..URL.."/", function(resp)
if resp.status == 200 then
print('Could connect to FritzBox, status ' .. resp.status)
print('Getting challenge key');
local challenge = string.match(resp.data, '"challenge":"([%x ]+)"');
if challenge==nil then
fibaro:debug('Could not get challenge');
fibaro:abort();
end
print(challenge);
x=challenge.."-"..PASSWORD;
x=UTF8To16(x,LE);
md5sum=md5.Calc(x);
print('MD5Sum: '..md5sum..' of string "'..x..'"');
response=challenge.."-"..md5sum;
postMethod("http://"..URL.."/login_sid.lua",'response='..response..'&username='..USERNAME, function(resp)
if resp.status==200 then
print('Login and forward successfully: '..resp.status);
local sid = string.sub(resp.data, 57, 72);
print('SID: >'..sid..'<');
if sid == "" or sid==nil then
print('Could not get SID');
print('Headers:');
tprint(resp,0);
fibaro:debug('Aborting');
fibaro:abort();
end;
getMethod("http://"..URL.."/net/network_user_devices.lua?sid="..sid.."",
function(resp)
-- Nothing to do above this line
------------------------------------------------------------------------------
------------------------------------------------------------------------------
--local tt = tonumber(getMethod("http://"..URL.."/webservices/homeautoswitch.lua?ain=..akt..&switchcmd=getswitchstate&sid="..sid..""))
--fibaro:debug("Zustand Aktor".. tt.." geschaltet")
getMethod("http://"..URL.."/webservices/homeautoswitch.lua?ain="..akt.."&switchcmd="..azu.."&sid="..sid.."")
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Nothing to do below this line
end,
function(err)
ShowMessage('debug', 'red', 'Reading error ' .. err);
tprint(resp,0);
end
)
else
ShowMessage('debug', 'red', 'Post failed not 303 but '..resp.status);
end
end,
function(err)
ShowMessage('debug', 'red', 'Error ' .. err);
end
);
else
print('Login failed: '..resp.status);
end
end,
function(err)
print('error ' .. err)
end
)