[DzVents] Animer des lumières (clignoter / transition ...)

Vous avez créé un script LUA dont vous êtes fier, un .sh génial, un programme Python hors du commun, un Tuto, c'est ici que vous pouvez les partager.
StephaneM
Messages : 14
Inscription : 14 nov. 2016, 00:53

[DzVents] Animer des lumières (clignoter / transition ...)

Message par StephaneM »

Salut,

Je partage avec vous un script DZVents de mon cru : une série de fonction pour animer vos ampoules, comme une transition, le clignotement, changement de couleurs etc...

Par exemple vous voulez être averti lorsque quelqu'un est de retour à la maison en ayant une ampoule qui clignote. (Attention, cet exemple utilise mon autre script de restauration d'état d'une ampoule)

Code : Tout sélectionner

return {
	on = {
		devices = { 'Présence Membre Famille' }
	},
	logging = {
		level = domoticz.LOG_INFO,
		marker = 'BLINKING TEST',
	},
	execute = function(domoticz, device)
		
	    
	    	if(device.isDevice) then
	        	domoticz.log('XXXX est rentré, on fait clignoter une ampoule du salon', domoticz.LOG_INFO)
	        
			-- Stocke l'état actuel de l'ampoule et programme sa restauration dans 35 sec.
	        	domoticz.helpers.restoreStateAfter(domoticz, 'Ampoule Salon', 35)
			
			-- Fais clignoter l'ampoule du salon pendant 30 secondes (clignote rouge / off, toutes les 0.5 sec
			-- de cette manière : rouge / off / rouge / off / rouge ....)
	        	domoticz.helpers.blink(domoticz, 'Ampoule Salon',
								{ ['r']=0,['g']=0,['b']=0,['br']=100,['cw']=0,['ww']=0,['t']=0,['m']=3 },
								{ ['r']=255,['g']=0,['b']=0,['br']=100,['cw']=0,['ww']=0,['t']=0,['m']=3 },
								0.5, 30)
	        
	        
        	end
        
	end
}
Différentes animations sont possibles :

Clignotement
Transition vers une autre couleur
Echanger les couleurs des lampes d'un même groupe / scène : très utile pour créer des effets guirlande de Noël et des petites animations facile à réaliser dans l'éditeur de groupes / scènes domoticz
Alterner les scènes / groupes : Pour créer des animations plus complexes (en fait une scène / groupe = un temps de l'animation...)

Pour l'installer :

Vous avez besoin de l'utilitaire CURL, vous pouvez télécharger CURL ici s'il n'est pas déjà installé sur votre système. CURL est disponible pour de mulitples plateforme (windows / linux). C'est un utilitaire ligne de commande pour faire des requêtes HTTP (entre autres), mon script l'utilise pour faire des requêtes HTTP synchrones (DZVents propose seulement des requêtes asynchrones). Une fois téléchargé, placez CURL dans le dossier de votre choix et notez le chemin vers CURL, on va en avoir besoin.

Vous devez aussi avoir un script DZVents "global_data". Si vous n'avez pas ce "global_data", rendez vous dans Domoticz dans "Configuration" -> "Plus d'Options" -> "Evénements", cliquez sur "+" et sélectionnez "DZVENTS" -> "GLOBAL DATA". Au lieu de "Script #1" tapez "global_data" (sans les guillemets). Copiez le code ci-dessous et cliquez sur "SAUVEGARDER" (vérifiez les logs de Domoticz s'il n'y a pas d'erreur, il y a quelque fois des problèmes de permissions quand Domoticz sauvegarde les scripts)

Si vous avez un script "global_data", alors vous devez ajouter les fonctions et variables globales que vous trouvez dans le code ci-dessous.

Code : Tout sélectionner

-- this scripts holds all the globally persistent variables and helper functions
-- see the documentation in the wiki
-- NOTE:
-- THERE CAN BE ONLY ONE global_data SCRIPT in your Domoticz install.

return {
	-- Variables Globales Persistentes
	data = { 
	         -- ANIMATE COLOR VARS --
	         cancelTransition = { initial = { [0] = nil } }
	         -- END ANIMATE COLOR VARS --
	         },

	-- Fonction Globales
	helpers = {
    
        -- Does the argument is a number
        -- x : the value to check
        isNumber = function (x)
            if tonumber(x) ~= nil then return true end
            return false
        end,
        
        -- get the color of a light
        -- deviceId : IDX or device name
        getColor = function(domoticz, deviceId)
            local idx = deviceId
            if(not domoticz.helpers.isNumber(deviceId)) then idx = domoticz.devices(deviceId).idx end
            cmd='"C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\curl.exe" '..domoticz.settings.url..'/json.htm?type=devices^&rid='..idx
            local httpRequest = assert(io.popen(cmd))
            local html = httpRequest:read('*all')
	        local json = domoticz.utils.fromJSON(html)
	        -- adding brigthness to the color object
	        local color = domoticz.utils.fromJSON(json.result[1].Color)
	        color.br = domoticz.devices(deviceId).level
	        -- returning the result
            return color
        end,
        
        -- ANIMATE COLOR FUNC --
        --
        cap255 = function(x)
            if(x>255) then return 255 end
            if(x<=0) then return nil end
            return math.floor(x)
        end,
        
        cap100 = function(x)
            if(x>100) then return 100 end
            if(x<=0) then return nil end
            return math.floor(x)
        end,
        
        -- Transition a light to a new color over time
        -- device : light to be transitionned
        -- color : the new color and brightness : { ['r']=0, ['g']=0, ['b']=0, ['br']=0, ['cw']=0, ['ww']=0, ['t']=0, ['m']=1/2/3/4 }
        -- m = 1, white only br means something, br 0 to 100
        -- m = 2, white temp only t mean something, t 0 to 255
        -- m = 3, color only r,g,b mean something, rgb 0 to 255
        -- m = 4, extended color only r,g,b,cw,ww mean something, cw,ww,rgb 0 to 255
        -- duration : duration of the effect in seconds
        -- NOTE : if the light is off, no transition is done other
        -- than brightness (basically Off = color 0,0,0)
        transitionTo = function(domoticz, device, color, duration)
            -- compute the number of steps needed
            -- minimum duration : 1 sec.
            -- max. steps number : 30
            local timeLapse = 1
            local totalSteps = 30
            if(duration >= 30) then
                timeLapse = duration / 30
                totalSteps = 30
            else
                timeLapse = 1
                totalSteps = duration
            end
            
            -- creating array of steps
            local startColor = domoticz.helpers.getColor(domoticz, device)
            -- special case : device is off
            if(domoticz.devices(device).inActive) then
                startColor.r = 0
                startColor.g = 0 
                startColor.b = 0 
                startColor.cw = 0 
                startColor.ww = 0 
                startColor.t = 0 
                startColor.br = 0
            end
            -- filling the array will all steps states
            local colorSteps = { ['next'] = nil, ['color'] = color}
            for step = totalSteps,1,-1 do
                local stepColor = { ['r']=domoticz.helpers.cap255(startColor.r + (step * (color.r-startColor.r)/totalSteps)),
                                    ['g']=domoticz.helpers.cap255(startColor.g + (step * (color.g-startColor.g)/totalSteps)),
                                    ['b']=domoticz.helpers.cap255(startColor.b + (step * (color.b-startColor.b)/totalSteps)),
                                    ['br']=domoticz.helpers.cap100(startColor.br + (step * (color.br-startColor.br)/totalSteps)),
                                    ['cw']=domoticz.helpers.cap255(startColor.cw + (step * (color.cw-startColor.cw)/totalSteps)),
                                    ['ww']=domoticz.helpers.cap255(startColor.ww + (step * (color.ww-startColor.ww)/totalSteps)),
                                    ['t']=domoticz.helpers.cap255(startColor.t + (step * (color.t-startColor.t)/totalSteps)),
                                    ['m']=startColor.m }
                colorSteps = { ['next'] = colorSteps, ['color'] = stepColor}
            end
            
            -- start the transition effect
            domoticz.emitEvent('transitionState', { ['steps'] = colorSteps, ['delay'] = timeLapse, ['device'] = device })
        end,
        
        -- make a light blink
        -- device : idx or device name to blink
        -- color1 : first color { ['r']=0, ['g']=0, ['b']=0, ['br']=0, ['cw']=0, ['ww']=0, ['t']=0, ['m']=1/2/3/4 }
        -- color2 : other color (blink = alternate color1 and color2)
        -- if one of the color has br=0 or r=0 and g=0 and b=0 then light is turn off (blinking on / off)
        -- speed : in seconds time between each color change
        -- duration : total duration of the effect in seconds (if 60 : the light will blink for a minute)
        blink = function(domoticz, device, color1, color2, speed, duration)
            
            local colorSteps = { ['next'] = color2, ['color'] = color1}
            
            domoticz.emitEvent('alternateState', { ['steps'] = colorSteps, ['delay'] = speed, ['duration'] = duration, ['device'] = device })
        end,
        
        -- swap the colors of each light in a group scenes
        -- group : idx or name of group / scene
        -- speed : in seconds, time between each color change
        -- duration : total duration of the effect in seconds
        animateGroup = function(domoticz, group, speed, duration)
            -- create the list of colors and states of all device in group
            -- when the animation starts
            local colors = {}
            local devices = {}
            local count = 0
            
            local idx = nil
            if(domoticz.utils.sceneExists(group)) then idx = domoticz.scenes(group).idx
            elseif(domoticz.utils.groupExists(group)) then idx = domoticz.groups(group).idx end
            
            -- We have a valid group or scene
            if(idx) then
                -- get the devices and state in the group / scene
                local cmd='"C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\curl.exe" '..domoticz.settings.url..'/json.htm?type=command^&param=getscenedevices^&isscene=true^&idx='..idx
                local httpRequest = assert(io.popen(cmd))
                local html = httpRequest:read('*all')
	            local json = domoticz.utils.fromJSON(html)
	            -- results are valid JSON
                if(json.status == 'OK') then
                    -- review each device and store its id and state (color / on/off...) in array
                    for i, device in ipairs(json.result) do
                        count = count + 1
                        -- device ID
                        devices[count] = device.DevID
                        -- device state : color / on/off / Level
                        if(device.Color) then device.Color = domoticz.utils.fromJSON(device.Color) end
                        if(device.Color) then device.Color.br = device.Level else device.Color = { ['br'] = device.Level } end
                        -- if device is not On then the device is off, we simplify by stating its brightness is 0
                        if(device.Command == "On") then colors[count] = device.Color else colors[count] = { ['br'] = 0 } end
                    end
                end
            end
            -- starting the transition
            domoticz.emitEvent('switchState', { ['devices'] = devices, ['colors'] = colors, ['step'] = 0, ['delay'] = speed, ['duration'] = duration })
        end,
        
        -- alternate scenes : change a scene each xx sec.
        -- scenes : scenes / groups array to alternat ex : {'Scene 1', 'Scene 2', 'Scene 3'}
        -- speed : in seconds, time between each scene change
        -- duration : total duration of the effect in seconds
        animateScenes = function(domoticz, scenes, speed, duration)
            
            domoticz.emitEvent('sceneChangeState', { ['scenes'] = scenes, ['step'] = 0, ['delay'] = speed, ['duration'] = duration })
        end,
        
        -- cancel a running transition
        -- work only for "transitionTo" et "blink"
        cancelTransition = function(domoticz, device)
            if(not domoticz.helpers.isNumber(device)) then device = domoticz.devices(device).id end
		    -- signal the transition to end early
		    domoticz.globalData.cancelTransition[device] = true
        end,
        --
        -- END ANIMATE COLOR FUNC
	    
	    
	}
}

Vous devez editer le code pour y remplacer le chemin vers CURL de votre domoticz, c'est à la ligne 30 dans l'éditeur qu'il faut faire le changement :

cmd='"C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\curl.exe" '..domoticz.settings.url..'/json.htm?type=devices^&rid='..idx

Remplacez ce chemin par le vôtre.

Lorsque c'est fait il faut ensuite créer le script principal : dans l'éditeur de scripts, cliquez sur "+" puis sélectionnez "DZVENTS" -> "CUSTOM EVENTS", donnez lui le nom que vous voulez à la place de "Script #1", par exemple "Transition Etat" (attention pas d'accents etc.. sinon ça coince), copiez / collez le code ci-dessous et cliquez sur "SAUVEGARDER".

Code : Tout sélectionner

return {
	on = { customEvents = { 'transitionState', 'alternateState', 'switchState', 'sceneChangeState' } },
	logging = { level = domoticz.LOG_ERROR, 
	            marker = 'TRANSITION STATE', },
	
	execute = function(domoticz, item)
	    
	    -- TRANSITION
		if (item.isCustomEvent and item.trigger == 'transitionState') then
		    -- Get JSON in event data
		    local eventData = domoticz.utils.fromJSON(item.data)
		    
		    -- should we cancel our transition ?
		    local deviceId = domoticz.devices(eventData.device).id
		    if(domoticz.globalData.cancelTransition[deviceId]) then
		        domoticz.globalData.cancelTransition[deviceId] = nil
		        return
		    end
		   
		    -- color data for the transition
		    local currentColor = eventData.steps.color
		    local nextColorSteps = eventData.steps.next 
		    
		    -- checking colors values (we use nil instead of 0 
		    -- as it shortens the string for the custom event)
		    if(not currentColor.r) then currentColor.r = 0 end
		    if(not currentColor.g) then currentColor.g = 0 end
		    if(not currentColor.b) then currentColor.b = 0 end
		    if(not currentColor.br) then currentColor.br = 0 end
		    if(not currentColor.cw) then currentColor.cw = 0 end
		    if(not currentColor.ww) then currentColor.ww = 0 end
		    if(not currentColor.t) then currentColor.t = 0 end
		    
		    -- setting color / state
		    if(domoticz.devices(eventData.device).deviceType == 'Color Switch') then
		        domoticz.helpers.setColor(domoticz, eventData.device, 
		                                  currentColor.r,currentColor.g,currentColor.b,
		                                  currentColor.br,
		                                  currentColor.cw,currentColor.ww,
		                                  currentColor.m,currentColor.t)
		    elseif(domoticz.devices(eventData.device).switchType =='Dimmer') then
		        domoticz.devices(eventData.device).dimTo(currentColor.br)
		    end
		    
		    -- schedule next transition
		    if(nextColorSteps) then
		        domoticz.emitEvent('transitionState', { ['steps'] = nextColorSteps, ['delay'] = eventData.delay, ['device'] = eventData.device }).afterSec(eventData.delay)
		    end
	    end
	
	    -- BLINKING
	    if (item.isCustomEvent and item.trigger == 'alternateState') then
	        -- Get JSON in event data
		    local eventData = domoticz.utils.fromJSON(item.data)
	        -- should we cancel our transition ?
	        if(eventData.duration <= 0) then return end
	        local deviceId = domoticz.devices(eventData.device).id
		    if(domoticz.globalData.cancelTransition[deviceId]) then
		        domoticz.globalData.cancelTransition[deviceId] = nil
		        return
		    end
		    
		    -- color date for the transition
		    local currentColor = eventData.steps.color
		    local nextColorSteps = eventData.steps.next
		    
		    -- swappping
		    eventData.steps.color = nextColorSteps
		    eventData.steps.next = currentColor
		    
		    -- reducing duration
		    eventData.duration = eventData.duration - eventData.delay
		    
		    -- checking colors values (we use nil instead of 0 
		    -- as it shortens the string for the custom event)
		    if(not currentColor.r) then currentColor.r = 0 end
		    if(not currentColor.g) then currentColor.g = 0 end
		    if(not currentColor.b) then currentColor.b = 0 end
		    if(not currentColor.br) then currentColor.br = 0 end
		    if(not currentColor.cw) then currentColor.cw = 0 end
		    if(not currentColor.ww) then currentColor.ww = 0 end
		    if(not currentColor.t) then currentColor.t = 0 end
	        
	        -- setting color / state
	        if((currentColor.r == 0 and currentColor.g == 0 and currentColor.b == 0 and currentColor.m == 3) or
	           (currentColor.br == 0)) then
	            domoticz.devices(eventData.device).switchOff()
	        elseif(domoticz.devices(eventData.device).deviceType == 'Color Switch') then
		        domoticz.helpers.setColor(domoticz, eventData.device, 
		                                  currentColor.r,currentColor.g,currentColor.b,
		                                  currentColor.br,
		                                  currentColor.cw,currentColor.ww,
		                                  currentColor.m,currentColor.t)
		    elseif(domoticz.devices(eventData.device).switchType =='Dimmer') then
		        domoticz.devices(eventData.device).dimTo(currentColor.br)
		    end
	        
	        --scheduling next event
	        domoticz.emitEvent('alternateState', { ['steps'] = eventData.steps, ['delay'] = eventData.delay, ['duration'] = eventData.duration, ['device'] = eventData.device }).afterSec(eventData.delay)
	    end
	    
	    -- GROUP SWAPPING
	    if (item.isCustomEvent and item.trigger == 'switchState') then
	        -- Get JSON in event data
		    local eventData = domoticz.utils.fromJSON(item.data)
		    
		    -- should we cancel our transition ?
	        if(eventData.duration <= 0) then return end
	        
	        -- compute number of steps
	        local colorCount = 0
	        for i, color in ipairs(eventData.colors) do
	            colorCount = colorCount + 1
	        end
		    
		    -- setting color and state
		    -- enum devices and choose state based on step number
		    for i, deviceId in ipairs(eventData.devices) do
		        deviceId = tonumber(deviceId)
		        
	            if(eventData.step >= colorCount) then eventData.step = 0 end
		        local colorIndex = i + eventData.step
		        if(colorIndex > colorCount) then colorIndex = colorIndex - colorCount end
		        
		        if(eventData.colors[colorIndex].br == 0)then
		            -- turning off
		            domoticz.devices(deviceId).switchOff()
		        elseif(eventData.colors[colorIndex].m) then
		            -- set color if device is capable of color
		            if(domoticz.devices(deviceId).deviceType == 'Color Switch') then
		                domoticz.devices(deviceId).setColor(eventData.colors[colorIndex].r,eventData.colors[colorIndex].g,eventData.colors[colorIndex].b,
		                                                    eventData.colors[colorIndex].br,
		                                                    eventData.colors[colorIndex].cw,eventData.colors[colorIndex].ww,
		                                                    eventData.colors[colorIndex].m,eventData.colors[colorIndex].t)
		            else
		                -- at least set brightness or turn the device on...
		                if(domoticz.devices(deviceId).switchType =='Dimmer') then
		                    domoticz.devices(deviceId).Level = eventData.colors[colorIndex].br
		                else
		                    domoticz.devices(deviceId).switchOn()
		                end
		            end
		        else
		            -- set level / turning on
		            if(domoticz.devices(deviceId).switchType =='Dimmer') then
		                domoticz.devices(deviceId).Level = eventData.colors[colorIndex].br
		            else
		                domoticz.devices(deviceId).switchOn()
		            end
		        end
	        end
	        -- schedule next transition
	        domoticz.emitEvent('switchState', { ['devices'] = eventData.devices, ['colors'] = eventData.colors, ['step'] = eventData.step + 1, ['delay'] = eventData.delay, ['duration'] = eventData.duration - eventData.delay }).afterSec(eventData.delay)
	    end
	    
	    -- CHANGEMENT DE SCENE
	    if (item.isCustomEvent and item.trigger == 'sceneChangeState') then
	        -- Get JSON in event data
		    local eventData = domoticz.utils.fromJSON(item.data)
		    
		    -- should we cancel our transition ?
	        if(eventData.duration <= 0) then return end
	        
	        -- compute number of steps
	        local sceneCount = 0
	        for i, scene in ipairs(eventData.scenes) do
	            sceneCount = sceneCount + 1
	        end
	        
	        -- selecting scene to apply / group to turn on
	        if(eventData.step >= sceneCount) then eventData.step = 0 end
	        local sceneIndex = 1 + eventData.step
	        if(sceneIndex > sceneCount) then sceneIndex = sceneIndex - sceneCount end
	        -- applying
	        if(domoticz.utils.sceneExists(eventData.scenes[sceneIndex])) then
	            domoticz.scenes(eventData.scenes[sceneIndex]).switchOn()
	        elseif(domoticz.utils.groupExists(eventData.scenes[sceneIndex])) then
	            domoticz.groups(eventData.scenes[sceneIndex]).switchOn()
	        end
	        -- schedule next transition
	        domoticz.emitEvent('sceneChangeState', { ['scenes'] = eventData.scenes, ['step'] = eventData.step + 1, ['delay'] = eventData.delay, ['duration'] = eventData.duration - eventData.delay }).afterSec(eventData.delay)
	    end
	end
}

Désormais dans vos scripts vous pouvez utiliser les fonctions suivantes :

domoticz.helpers.transitionTo(domoticz, device, color, duration)
Change progressivement la couleur de l'ampoule vers la nouvelle couleur. Vous pouvez choisir la durée de l'effet (en secondes). La couleur est dans le format habituel { ['r']=0, ['g']=0, ['b']=0, ['br']=0, ['cw']=0, ['ww']=0, ['t']=0, ['m']=1/2/3/4 }.
Vous pouvez effectuer une transition vers l'état éteint simplement en utilisant la couleur noir ou la luminosité à 0.

domoticz.helpers.blink(domoticz, device, color1, color2, speed, duration)
Fait clignoter une lumière. On peut choisir deux couleurs à alterner ainsi que la vitesse de changement et la durée totale du clignotement. Si une des couleurs est noire ou que la luminosité est à 0 alors un clignotement allumé / éteint est effectué.

domoticz.helpers.animateGroup(domoticz, group, speed, duration)
Echange la couleur de chaque lampe au sein d'un groupe / scène. Vous pouvez choisir la vitesse et la durée de l'effet

domoticz.helpers.animateScenes(domoticz, scenes, speed, duration)
Alterne les scènes / groupes : Change de scène toutes les xx secondes. Vous choisissez xx (speed) et la durée totale de l'effet. C'est un tableau de scènes / groupes qu'il faut donner

domoticz.helpers.cancelTransition(domoticz, device)
Annule une animation en cours pour une ampoule. Ne fonctionne qu'avec blink et transitionTo.
Répondre