dzVents Bayesian Sensor

Easy to use, 100% Lua-based event scripting framework.

Moderator: leecollings

Post Reply
DanM
Posts: 79
Joined: Thursday 23 October 2014 22:41
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

dzVents Bayesian Sensor

Post by DanM »

So recently I took a trip to the dark side and briefly flirted with Home Assistant in place of Domoticz. The fact that Im here tells you a lot about how that went, but there was one feature that I really missed - a bayesian probability based sensor. Simply put this is a sensor that uses probability to determine whether it should be on or off. It allowed me to determine presence in a room based on not only on that rooms motion sensor, but motion in other rooms, the TV being on, the heating setting etc. Long story short, below is my attempt to create this in dzvents. Any improvements and or edits are gratefully received. Ill do my best to answer any questions (but we have a baby over due so it might take me a while to get back here).

Rather than me try to explain how this works, feel free to take a look at this written by a member of the home assistant community:
http://nbviewer.jupyter.org/github/robm ... rial.ipynb

The script is partly its own script, but most of it resides in the global_data.lua file as a helper script. The actual probabilities in my script are literally random numbers Ive used for testing - swapping my devices for yours wont work Im afraid.

Main code:

Code: Select all

--**********************************
-- DESCRIPTION:
-- This script aims to use probability to determine whether a room is occupied. It is based upon the 
-- home assistant script that operates in a smiliar way. Devices effect the probability in two ways
-- either when they are on, or if they were last seen between certain times. The script requires the following:

-- VARIABLES:
-- room name: The name of the room, set the name of the switch the script triggers
-- prior: The prior probability of the event. At any point in time (ignoring all external influences) 
-- how likely is this event to occur?
-- threshold: The probability at which the sensor should trigger to on.

--devicesToCheckOn : An array containing the following:
    -- Name: Name of the entity to monitor.
    -- true_prob (Required): The probability of the observation occurring, given the event is true.
    -- false_prob (Optional): The probability of the observation occurring, given the event is false.
    
--deviceTimesToCheckLastSeen : An array containing the following:
    -- Name: Name of the entity to monitor.
    -- true_prob (Required): The probability of the observation occurring, given the event is true.
    -- false_prob (Optional): The probability of the observation occurring, given the event is false.
    -- startmin: the start of the range that will trigger this rule
    -- endmin: the end of the range that will trigger this rule
    -- E.G if startmin is 0 and endmin 5 then switch seen in the last 5 minutes triggers the probability  
    
-- Why have timer rules?
-- We do this to increase accuracy. The probability model works MUCH better with more inputs

--What are the outputs?
-- None, the script triggers a switch that takes the name 'room_name Occupied' - ie 'Living Room Occupied' this will need to be created. 

-- Why do you have so many devices as triggers?
-- the triggers have to be set to anything that can effect the probability so a VERY long list. Because of this I would recommend 
-- starting the script name with 'Z' so it triggers last. 

--**********************************



return {
	on = {
		devices = {
			'Conservatory Motion Sensor', 
            'Kitchen Motion Sensor', 
	        'Bedroom Motion Sensor', 
            'Living Room Motion Sensor (Main)',
            'Upstairs Hall Motion Sensor (Bottom)',
            'Downstairs TV',
            'Living Room Motion Sensor (Desk)'},
         timer = {
         'every minute'
            }
        },
	 data = {
           counter = { initial = 0 }
       },
	execute = function(domoticz, device)

        --**********************************
        --
        -- Living Room
        --
        --**********************************
       
       --set the room name 
        local room_name = "Living Room" 
        
        -- Set Devices that we want to check the last seen for
        local deviceTimesToCheckLastSeen= {
        	{ ['name'] = 'Conservatory Motion Sensor', ['true_prob'] = 0.5,['false_prob'] = 0.3,['startmin'] = 0,['endmin'] = 5 },
        	{ ['name'] = 'Conservatory Motion Sensor', ['true_prob'] = 0.5,['false_prob'] = 0.3,['startmin'] = 5,['endmin'] = 15 }
        }
        
        -- Set Devices that we want to check are on
        local devicesToCheckOn = {
        	{ ['name'] = 'Conservatory Motion Sensor', ['true_prob'] = 0.5,['false_prob'] = 0.3 },
        	{ ['name'] = 'Kitchen Motion Sensor', ['true_prob'] = 0.75,['false_prob'] = 0.5 },
        	{ ['name'] = 'Bedroom Motion Sensor', ['true_prob'] = 0.2,['false_prob'] = 0.7 },
        	{ ['name'] = 'Living Room Motion Sensor (Main)', ['true_prob'] = 1,['false_prob'] = 0 },
        	{ ['name'] = 'Living Room Motion Sensor (Desk)', ['true_prob'] = 1,['false_prob'] = 0 },	
        	{ ['name'] = 'Upstairs Hall Motion Sensor (Bottom)', ['true_prob'] = 0.75,['false_prob'] = 0.1 },
        	{ ['name'] = 'Downstairs TV', ['true_prob'] = 0.75,['false_prob'] = 0.25 }	
        }
        
        -- Set the starting probability
        local prior = 0.5
        
        -- Set the activation threshold
        local threshold = 0.85
        
        domoticz.helpers.occupied(domoticz, devicesToCheckOn, deviceTimesToCheckLastSeen, room_name, prior,threshold)

	end -- end dzvents
}
global_data:

Code: Select all

return {
   helpers = {
      bayes = function(prior, true_prob, false_prob)
            numerator = prior * true_prob
    		denominator = numerator + false_prob * (1- prior)
    		prior = numerator / denominator
    		return prior
  end,
  
    occupied = function(domoticz, devicesToCheckOn, deviceTimesToCheckLastSeen, room_name,prior,threshold)
      local text_for_log= ""
    
    --Start by looping through all the motion sensors 
    	for i, deviceToCheck in pairs(devicesToCheckOn) do
            old_prior = prior
    		if domoticz.devices(deviceToCheck['name']).state == 'On' then
                prior = domoticz.utils.round(domoticz.helpers.bayes(prior,  deviceToCheck['true_prob'], deviceToCheck['false_prob']),3)
                text_for_log = text_for_log .."\n" .. deviceToCheck['name'] .. " On: " .. "Old prior was: " .. old_prior .. ". New prior is : " .. prior
			end   
		end	


    --Start by looping through all the devies with time settings
    	for i, deviceToCheck in pairs(deviceTimesToCheckLastSeen) do
  	        local device_last_seen = domoticz.devices(deviceToCheck['name']).lastUpdate.minutesAgo 
  		    if device_last_seen  >= deviceToCheck['startmin'] and device_last_seen < deviceToCheck['endmin'] then
                old_prior = prior
                prior = domoticz.utils.round(domoticz.helpers.bayes(prior,  deviceToCheck['true_prob'], deviceToCheck['false_prob']),3)
                text_for_log = text_for_log .."\n" ..("Motion between " .. deviceToCheck['startmin'] .. " and " .. deviceToCheck['endmin'] ..  " minutes, old prior was: " .. old_prior .. ".New prior: " .. prior)    
            end
    	end		
	

    --update the text (I use a switch, the current setting is to log)
    --domoticz.devices(room_name ..  " Bayesian").updateText(text_for_log)
	print(text_for_log)

	--finally decide if the room is occupied
			if prior >= threshold then
			    domoticz.devices(room_name .. " Occupied").switchOn().checkFirst()
		    else
			    domoticz.devices(room_name ..  " Occupied").switchOff().checkFirst()		        
			end
	end
			
   }
}
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest