Aller au contenu
Nikko

Backup Via Scene Lua

Recommended Posts

Bonjour,

 

Voici un résumé sur la réalisation/suppression de backups via des scènes en Lua. La création/suppression de backup s'effectue en temps normal sur l'interface du HomeCenter à  la page Diagnostiques/Sauvegarde et restauration.

 

Le code donné dons les 2 scènes n'est pas des plus optimisé; cela constitue juste une base.

 

EDIT (03/12/2016): Mise à jour avec l'api pour les versions >=4.101; voir plus loin: (https://www.domotique-fibaro.fr/topic/8641-backup-via-scene-lua/?do=findComment&comment=146362)

 

Les Commandes de l'API

=> Comme les urls de l'api vont être appelées depuis des scènes lua, l'ip de la box utilisée sera 127.0.0.1 (port 11111)

=> En cas d'appel sur l'ip LAN de la box (Ex: http://192.168.0.100/api/settings/backups) , il faudra en plus transmettre l'authentification dans la requète.

 

 

Attention: Lors de la création de Backup, "tout le reste" est suspendu; de même après que le backup soit effectué, le moteur Z-Wave et d'autres services redémarrent. Donc en cas de planification , privilégier la nuit, là  ou l'activité ZWave, scénarios, est faible/inexistante.

 

 

Scène de création d'un backup

 

Exemple de Scène de création d'un Backup, avec notification vers portable:        
 

--[[
%% properties
%% events
%% globals
--]]
     
-- ID des mobiles,tablettes pour notification
local portable = { 385,378 }

-- Message Descriptif du Backup
local descriptif = 'Backup du '..os.date("%d/%m/%y - %HH%M")

function sendPush(message)
    if #portable > 0 then
        for _,v in ipairs(portable) do
            fibaro:call(v,'sendPush', message)
        end
    end
end

local url = 'http://127.0.0.1:11111/api/settings/backups'
local httpClient = net.HTTPClient()
httpClient:request(url , {
        success = function(response)
                    if tonumber(response.status) == 201 then
                        print("Backup Created at " .. os.date())
                        sendPush(descriptif .. ' effectué')
                    else
                        print("Error " .. response.status)
                        sendPush('Erreur lors de la création du Backup')
                    end
                end,
        error = function(err)
                    print('error = ' .. err)
                end,
        options = {
                method = 'POST',
                headers = {
                        ["content-type"] = 'application/x-www-form-urlencoded;'
                          },
                data = 'action=create&description='..descriptif
            }
});

Depuis la page des backups, on peut vérifier que la scène fonctionne:

 

IHM_ListeBackup.png

 

 

Pour les utilisateurs de GEA, on peut déclencher cette sauvegarde le 1er samedi de chaque mois par exemple:

-- Fonction déterminant si nous sommes le 1er samedi du mois
function isFirstSaturday()
	local t = os.date('*t')
	return ( t['day'] < 8 and t['wday'] == 7 )
end
	
-- Backup le 1er samedi du mois
GEA.add({"Function",function() return isFirstSaturday() end} , 30 , "Backup Mensuel du HC2" , {{"Time","01:00","01:01"},{"Scenario", 12}})

.

Scène de suppression d'un backup

 

Exemple de Scene Lua pour réaliser la suppression du backup le plus ancien:

 

--[[
%% properties
%% events
%% globals
--]]

-- Flag dryrun; Si true, la requete sur api pour effacer le backup n'est pas effectuée
local dryrun = true

-- Récupération de la list des backups
local backups = api.get('/settings/backups')

-- Vérification de présence Backup
if (backups and type(backups == 'table') and #backups > 0) then
    if #backups > 1 then print(#backups .. ' Backups présents') else print('1 Backup présent') end
    
    for i in ipairs(backups) do
        fibaro:debug('ID: '..backups[i]['id']..' | TIMESTAMP: '..backups[i]['timestamp']..' | DESCRIPTION: '..backups[i]['description'])
    end
else
    print('Pas de backup ou erreur lors de la récupération de la liste')
    fibaro:abort()
end


-- Pour chaque Backup on stock le timestamp de la date de réalisation dans une table
-- On classe ensuite les timestamps par ordre croissant
local timestamp = {}
for i in ipairs(backups) do
    table.insert(timestamp, backups[i]['timestamp'])
end
table.sort(timestamp)


-- Le 1er timestamp de la table est le plus petit donc le plus ancien en epochtime
-- Dans la table de backup on recherche le backup ayant ce timestamp
local id = false
for i in ipairs(backups) do
    if (tonumber(backups[i]['timestamp']) == tonumber(timestamp[1])) then id = backups[i]['id'] end
end

if id then
    print("ID du backup le plus ancien: "..id)
else
    print("Erreur lors de la récupération de l'ID")
    fibaro:abort()
end


-- Requete via API pour effacer le backup le plus ancien
if not dryrun then
    local url = 'http://127.0.0.1:11111/api/settings/backups?id='..id
    local httpClient = net.HTTPClient()
    httpClient:request(url , {
        success = function(response)
                    if tonumber(response.status) == 200 then
                        print("Backup deleted at " .. os.date())
                    else
                        print("Error " .. response.status)                        
                    end
                end,
        error = function(err)
                    print('error = ' .. err)
                end,
        options = {
                method = 'DELETE',
                headers = {
                    ["content-type"] = 'application/x-www-form-urlencoded;'
                            },
                data = 'id='..id
            }
    });
end

.

Cette Scène produit le debug suivant:

 

IHM_DEBUG_Scene_DEL_Backup_nodryrun.png

 

 

Nicolas

  • Upvote 12

Partager ce message


Lien à poster
Partager sur d’autres sites

merci, bien pratique effectivement

je mets en place

 

mais je vais faire des backup hebdo, jamais assez prudent avec la HC2

-- Backup Auto HC2
    GEA.add(true, 30, "Backup Hebdo HC2" , {{"Days", "Thursday"},{"Time","12:10","12:11"},{"Scenario", 312}})  
    GEA.add(true, 30, "Delete Bakcup HC2", {{"Days", "Thursday"},{"Time","12:05","12:05"},{"Scenario", 313}})  

Du coup, il faut mettre la scene en 'MANUELLE" ? avec la nouvelle option de la HC2

Partager ce message


Lien à poster
Partager sur d’autres sites
 

Alors le backup auto fonctionne bien mais le Delete non

 

j'ai ca dans le log

il n'execute pas la DELETE.

[DEBUG] 13:11:33: 13 Backups présents
[DEBUG] 13:11:33: ID: 452 | TIMESTAMP: 1468491024 | DESCRIPTION: Backup du 14/07/16 - 12H10
[DEBUG] 13:11:33: ID: 451 | TIMESTAMP: 1468411713 | DESCRIPTION: Stable
[DEBUG] 13:11:33: ID: 449 | TIMESTAMP: 1468166875 | DESCRIPTION: Stable 4.090
[DEBUG] 13:11:33: ID: 448 | TIMESTAMP: 1468161140 | DESCRIPTION: 4.083->4.090
[DEBUG] 13:11:33: ID: 445 | TIMESTAMP: 1467471985 | DESCRIPTION: 4.080->4.083
[DEBUG] 13:11:33: ID: 450 | TIMESTAMP: 1465737177 | DESCRIPTION: Stable
[DEBUG] 13:11:33: ID: 447 | TIMESTAMP: 1457115357 | DESCRIPTION: 4.070->4.080
[DEBUG] 13:11:33: ID: 457 | TIMESTAMP: 1454093563 | DESCRIPTION: 4.056->4.070
[DEBUG] 13:11:33: ID: 446 | TIMESTAMP: 1451844568 | DESCRIPTION: V2015 Optimisée
[DEBUG] 13:11:33: ID: 453 | TIMESTAMP: 1440080155 | DESCRIPTION: 4.049->4.056
[DEBUG] 13:11:33: ID: 455 | TIMESTAMP: 1429877186 | DESCRIPTION: 4.040->4.042
[DEBUG] 13:11:33: ID: 456 | TIMESTAMP: 1427395206 | DESCRIPTION: 4.036->4.040
[DEBUG] 13:11:33: ID: 454 | TIMESTAMP: 1424505690 | DESCRIPTION: 3.600->4.033
[DEBUG] 13:11:33: ID du backup le plus ancien: 454

en prime des icônes pour les scenes

 

icon trash It

SAve

36080

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Salut,

 

Le code fonctionne chez moi, mais comme je n'ai pas pu l'utiliser chez plusieurs personnes, j'ai mis une "sécurite": la variable dryrun. Si sur true le delete n'est pas executé si sur false le delete se fait.

C'est justement pour vérifier que l'id retournée correspond bien au backup le plus ancien: ça évite de supprimer si erreur. as tu bein basculé le flag dans le script ?

Partager ce message


Lien à poster
Partager sur d’autres sites

désolé j'avais pas vu le flag

 

c'est parfait c'est 2 scenes !

Partager ce message


Lien à poster
Partager sur d’autres sites

Perso c'est backup toutes les semaines, dans la nuit de samedi àdimanche.

En plus, le backup ne démarre que si la Variable globale VACANCES = 0, afin de ne pas risque de rendre la box instable durant mon absence. On a déjàvu un simple backup faire planter le moteur Z-Wave.....

Partager ce message


Lien à poster
Partager sur d’autres sites

Salut @Niko

 

penses-tu qu'il est possible de réaliser la sauvguard sur un serveur plutôt que sur la clé ?

Partager ce message


Lien à poster
Partager sur d’autres sites

@971jmd,

 

L'api ou l'utilisation de l'interface fibaro ne permet pas de le faire.

Il existe des possibilités en ayant l'accès root sur la box, mais je ne m'étendrai pas sur le sujet ;)

Partager ce message


Lien à poster
Partager sur d’autres sites

Oui merci

Envoyé de mon iPhone en utilisant Tapatalk

Partager ce message


Lien à poster
Partager sur d’autres sites

SAlut @Nikko, j'avais a-pas vu..top

 

Merci beaucoup, on en avait parlé et tu l'as fait !! c'est MEGA TOP ;-) merci vraiment.

 

@971jmd, possible si on est root ;-)

  • Upvote 1

Partager ce message


Lien à poster
Partager sur d’autres sites

super intéressant, merci beaucoup, je met en place dès ce soir :)

 

Merci !

Partager ce message


Lien à poster
Partager sur d’autres sites

Un grand merci !!

 

Mis en place Nickel comme toujours avec les experts !!!

 

Au top

Partager ce message


Lien à poster
Partager sur d’autres sites

Super en place chez moi

Je verrais comment cela fonctionne en auto via GEA samedi prochain (sic)

Partager ce message


Lien à poster
Partager sur d’autres sites

SAlut @Nikko, j'avais a-pas vu..top

Merci beaucoup, on en avait parlé et tu l'as fait !! c'est MEGA TOP ;-) merci vraiment.

@971jmd, possible si on est root ;-)

Salut, Qu'appelle tu Root?

Envoyé de mon iPhone en utilisant Tapatalk

Partager ce message


Lien à poster
Partager sur d’autres sites

l'API a du changer car cela ne marche plus depuis la V4.101. :(.

  • Upvote 1

Partager ce message


Lien à poster
Partager sur d’autres sites

Oui, Nikko a donné la nouvelle API :

 

 

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Ah, @Lazer plus rapide ;-)

 

API du backup : http://homecenter/api/service/backups

Partager ce message


Lien à poster
Partager sur d’autres sites

Bonjour

 

Mise à jour: Suite au passage en 4.101 et plus, l'API a changé concernant les backups; voici donc un résumé en fonction des versions


API pour Version < 4.101

 

BACKUP:

  • URL: http://ip/api/settings/backups
  • TYPE: POST
  • DATA: action=create&description=monbackup
  • HEADER: Content-type: application/x-www-form-urlencoded

RESTORE:

  • URL: http://ip/api/settings/backups
  • TYPE: POST
  • DATA: action=restore&id=1051 (par exemple)
  • HEADER: Content-type: application/x-www-form-urlencoded

DELETE:

  • URL: http://ip/api/settings/backups?id=xxxx    (xxx: id du backup)
  • TYPE: DELETE
  • DATA: id=xxxx
  • HEADER: Content-type: application/x-www-form-urlencoded        

    
API pour Version >= 4.101


BACKUP:

  • URL: http://ip/api/service/backups/
  • TYPE: POST
  • DATA: {"action":"create","params":{"name":"Le descriptif de mon backup"}}
  • HEADER:
    • Content-type: application/x-www-form-urlencoded
    • Authorization: Basic xxxxxxxxxxxxxxxx (mot de passe encodé en base64)

            
RESTORE:

  • URL: http://ip/api/service/backups/
  • TYPE: POST
  • DATA: {"action":"restore","params":{"id":1054}} (1054 est un exemple)
  • HEADER:
    • Content-type: application/x-www-form-urlencoded
    • Authorization: Basic xxxxxxxxxxxxxxxx (mot de passe encodé en base64)

 

DELETE:

  •  URL: http://ip/api/service/backups/
  •  TYPE: DELETE
  •  DATA: n/a
  •  HEADER: Authorization: Basic xxxxxxxxxxxxxxxx (mot de passe encodé en base64)

 

Encodage Base64

 

Pour le couple login/password, ils doivent être encodés en base64; Pour ce faire:

  • Sous linux: echo -n 'monlogin@fai.com:motdepasse' | base64
  • Sous windows:
    • une scene en lua sur le HC2 : taper base64.lua sous google
    • Le toolkit de @Krikroff (https://www.domotique-fibaro.fr/topic/239-hc2-toolkit-application-v1203/)
    • Advanced RESTful client: faire une requete sur une url de l'api, le couple login/mdp sera demandé et sera affiché ensuite encodé.

 

Exemple de Scènes

 

Pour des requêtes externes, cela ne change pas vraiment. En revanche pour ceux qui comptent réaliser des scènes de backup/delete, ces requètes ne sont plus gérées pareil (avant en local, nous utilisions 127.0.0.1:11111 qui est géré par le process HCServer; maintenant il faut utiliser 127.0.0.1 et pointer sur un php)


Donc pour les scènes lua, pas de api.post ou api.get et authentification nécessaire.

 

Voici 2 scènes exemples; Les scène ne sont pas forcèment optimisées. Elles montrent juste l'utilisation de l'api

 

Scène de Backup: performBackup.lua

--[[
%% properties
%% events
%% globals
--]]
     
-- Message Descriptif du Backup
local descriptif = 'Backup du '..os.date("%d/%m/%y - %HH%M")

-- Password admin encodé en base64
local password = 'aWRpb3RAZ3Z1bGF2aWVyZ2UuY29tOnR1X3lfY3JveWFpc19oZWlu'

local url = 'http://127.0.0.1/api/service/backups'
local datas = '{"action":"create","params":{"name":"'..descriptif..'"}}'


local httpClient = net.HTTPClient()
httpClient:request(url , {
		success = function(response)
					if tonumber(response.status) == 201 or tonumber(response.status) == 202then
						print("Backup Created at " .. os.date())
						
                    else
						print("Error " .. response.status)						
                    end
                end,
        error = function(err)
					print('error = ' .. err)
                end,
        options = {
				method = 'POST',
                headers = { 
						["content-type"] = 'application/json',
						["Authorization"] = 'Basic '..password
                          },
                data = datas
			}
});

 

Scene d'effacement du backup le plus ancien: deleteBackup.lua

 

--[[
%% properties
%% events
%% globals
--]]


-- Flag dryrun; Si true, la requete sur api pour effacer le backup n'est pas effectuée
local dryrun = true

-- Password admin encodé en base64
local password = 'aWRpb3RAZ3Z1bGF2aWVyZ2UuY29tOnR1X3lfY3JveWFpc19oZWlu'

function sortBackup(data)
	local backups = json.decode(data)
	-- Vérification de présence Backup
	if (backups and type(backups == 'table') and #backups > 0) then
		if #backups > 1 then print(#backups .. ' Backups présents') else print('1 Backup présent') end
	
		for i in ipairs(backups) do
			fibaro:debug('ID: '..backups[i]['id']..' | TIMESTAMP: '..backups[i]['timestamp']..' | DESCRIPTION: '..backups[i]['description'])
		end
	else
		print('Pas de backup ou erreur lors de la récupération de la liste')
		fibaro:abort()
	end
	
	-- Pour chaque Backup on stock le timestamp de la date de réalisation dans une table
	-- On classe ensuite les timestamps par ordre croissant
	local timestamp = {}
	for i in ipairs(backups) do
		table.insert(timestamp, backups[i]['timestamp'])
	end
	table.sort(timestamp)


	-- Le 1er timestamp de la table est le plus petit donc le plus ancien en epochtime
	-- Dans la table de backup on recherche le backup ayant ce timestamp
	local id = false
	for i in ipairs(backups) do
		if (tonumber(backups[i]['timestamp']) == tonumber(timestamp[1])) then id = backups[i]['id'] end
	end

	if id then 
		print("ID du backup le plus ancien: "..id) 
		deleteBackup(id)
	else
		print("Erreur lors de la récupération de l'ID")
		fibaro:abort()
	end

end

function deleteBackup(id)
	-- Requete via API pour effacer le backup le plus ancien
	if (not dryrun) and (id) then
		print('Effacement du backup '..id..' en cours. Cela peut prendre 30s')
		local url = 'http://127.0.0.1/api/service/backups/'..id
		local httpClient = net.HTTPClient()
		httpClient:request(url , {
			success = function(response)
						if tonumber(response.status) == 200 then
							print("Backup deleted at " .. os.date())
						else
							print("Error " .. response.status)						
						end
					end,
			error = function(err)
						print('error = ' .. err)
					end,
			options = {
					method = 'DELETE',
					headers = {
						["content-type"] = 'application/x-www-form-urlencoded;',
						["authorization"] = 'Basic '..password
							},
					data = 'id='..id
				}
		})
	end
end

-- Récupération de la list des backups
local GETClient = net.HTTPClient()
if dryrun then print('Mode DryRun -> La requète pour effacer ne sera pas éxécutée') end
GETClient:request('http://127.0.0.1/api/service/backups', {
	success = function(response)
				if tonumber(response.status) == 200 then
					sortBackup(response.data)
				else
					print("Error " .. response.status)						
                   end
                end,
	error = function(err)
				print('error = ' .. err)
            end,
	headers = {
				["content-type"] = 'application/x-www-form-urlencoded;',
				["authorization"] = 'Basic '..password
				}				      	
	});
	

 

Nicolas

 

 

  • Upvote 3

Partager ce message


Lien à poster
Partager sur d’autres sites

YEs @Nikko, un grand merci, c'est top !!! 

 

encore en 4.100 mais on garde sous le coude lol

T'es au top..

 

Je mettrai à jour le post API des que possible

Partager ce message


Lien à poster
Partager sur d’autres sites

@Nikko

 

J'ai essaye cela en lua, mais rien, ca importe pas la librairie. Donc pas de mime dans le lua fibaro ?

 

local mime = require( "mime" )
local encodedString = mime.b64("Hello World");

print( mime.unb64( encodedString ) ) 

L'erreur 

[DEBUG] 22:11:31: line 47: attempt to call global 'require' (a nil value)
 

Partager ce message


Lien à poster
Partager sur d’autres sites

require fait parti des fonctions LUA bloquées par Fibaro

Partager ce message


Lien à poster
Partager sur d’autres sites

@pepite:

 

Source: https://gist.github.com/bortels/1436940

 

local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

-- encoding
function enc(data)
    return ((data:gsub('.', function(x) 
        local r,b='',x:byte()
        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#data%3+1])
end

print(enc('pepite@domotique-fibaro.fr:jolipassword'))

 

  • Upvote 3

Partager ce message


Lien à poster
Partager sur d’autres sites

Merci !!!!

 

heur c'était plus simple avec le require non ? :60::60:

 

allez je teste hihi

Partager ce message


Lien à poster
Partager sur d’autres sites

Bon, ben ok pour l'encoder, mais pas le decode, j'ai juste repique le code du GitHub ;-)

local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- encoding
function enc(data)
    return ((data:gsub('.', function(x) 
        local r,b='',x:byte()
        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#data%3+1])
end

-- decoding
function dec(data)
    data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(7-i) or 0) end
return string.char(c)
end))
end
 
print(enc('pepite@domotique-fibaro.fr:jolipassword'))
print(dec('cGVwaXRlQGRvbW90aXF1ZS1maWJhcm8uZnI6am9saXBhc3N3b3Jk'))

Ok pour l'encode, mais pas le decode lol

 

DEBUG] 22:45:54: cGVwaXRlQGRvbW90aXF1ZS1maWJhcm8uZnI6am9saXBhc3N3b3Jk
[DEBUG] 22:45:54: 8284:2 2868:48:234109839586480::<892

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Fonctionne parfaitement avec celui-ci :http://lua-users.org/wiki/BaseSixtyFour


 

-- character table string
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-- encoding
function enc(data)
    return ((data:gsub('.', function(x) 
        local r,b='',x:byte()
        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#data%3+1])
end
-- decoding
function dec(data)
    data = string.gsub(data, '[^'..b..'=]', '')
    return (data:gsub('.', function(x)
        if (x == '=') then return '' end
        local r,f='',(b:find(x)-1)
        for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
        return r;
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
        if (#x ~= 8) then return '' end
        local c=0
        for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
        return string.char(c)
    end))
end
-- command line if not called as library
if (arg ~= nil) then
    local func = 'enc'
    for n,v in ipairs(arg) do
        if (n > 0) then
            if (v == "-h") then print "base64.lua [-e] [-d] text/data" break
            elseif (v == "-e") then func = 'enc'
            elseif (v == "-d") then func = 'dec'
            else print(_G[func](v)) end
        end
    end
end
 
print(enc('pepite@domotique-fibaro.fr:jolipassword'))
print(dec('cGVwaXRlQGRvbW90aXF1ZS1maWJhcm8uZnI6am9saXBhc3N3b3Jk'))

Le debug

 

print(enc('pepite@domotique-fibaro.fr:jolipassword'))
print(dec('cGVwaXRlQGRvbW90aXF1ZS1maWJhcm8uZnI6am9saXBhc3N3b3Jk'))

 

Partager ce message


Lien à poster
Partager sur d’autres sites

×