Fixa spikar i avläsning med Lua (Domoticz) från onewire

OWFS - One Wire File System är en Linuxmjukvara som stödjer de flesta kommersiellt tillgängliga 1wire-enheterna. OWFS saknar helt möjligheter att presentera data - detta måste göras med tex RRDTool

Moderator: elf98

Kategoriregler
Vill du visa bilder i ditt inlägg? Använd funktionen "Ladda upp bilaga" nedanför textrutan!
Erik-NA
Tar hemautomation på allvar
Inlägg: 85
Blev medlem: 24 feb 2010, 22:19
Ort: Brottby
Kontakt:

Fixa spikar i avläsning med Lua (Domoticz) från onewire

Inlägg av Erik-NA » 28 jun 2016, 09:56

Har ett gäng DS2438 som mäter luftfuktighet och temperatur. De är alla anslutna via en onewirehub (hobbyboards) och rätt ofta får jag irriterande spikar på fukt och temperatur.

Skrev ihop ett LUA-skript för att råda bot på detta. Skriptet körs varje minut i Domoticz som uppdaterar en "device" i Domoticz för både temperatur och fuktighet.

Skriptet konfigurerar också hubben så att alla portar öppnas.

Kod: Markera allt

-- OWFS path
OWFS_PATH = "/mnt/1wire/uncached"
OWFS_DEVICES = {
                  ['26.160592010000'] = 'Krypgrund_SO',
                  ['26.740592010000'] = 'Krypgrund_SV',
                  ['26.802692010000'] = 'Krypgrund_NO'
              }

-- Onewire hub file
ONEWIRE_HUB_CONFIG_FILE       = "/mnt/1wire/uncached/EF.117120150000/hub/branch.BYTE"
ONEWIRE_HUB_PORT_CONFIG_VALUE = "15"
-- Cache file dir
OWFS_CACHEPATH = '/tmp'

-- Set how many readings in buffer
OWFS_BUFFERSIZE = 10

-- FUNCTIONS START --

--integer converts a float into an integer
function integer(x)
    return x<0 and math.ceil(x) or math.floor(x)
end

 -- deviceToTime returns a device time string in a format which can be used by the os.difftime function
function deviceToTime(time)
   return os.time { year  = string.sub(time, 1, 4),
                    month = string.sub(time, 6, 7),
                    day   = string.sub(time, 9, 10),
                    hour  = string.sub(time, 12, 13),
                    min   = string.sub(time, 15, 16),
                    sec   = string.sub(time, 18, 19) }
end

local function ReadFile(path)
    local file = io.open(path, "rb") -- r read mode and b binary mode
    if not file then
      return nil
    end
    local content = file:read "*a" -- *a or *all reads the whole file
    file:close()
    return content
end

local function WriteFile(path, content)
    local file = io.open(path, "wb") -- w write mode and b binary mode
    if not file then
      return nil
    end
    file:write(content)
    file:close()
end

local function ReadOWFS(device, filename, stop)
   value = ReadFile(string.format("%s/%s/%s", OWFS_PATH, device, filename))
   if (value ~= nil) then
         return value
   else
		-- Check if hub is configured with uncorrect value
		fileContent = ReadFile(ONEWIRE_HUB_CONFIG_FILE)
		if (fileContent ~= ONEWIRE_HUB_PORT_CONFIG_VALUE) then
			WriteFile(ONEWIRE_HUB_CONFIG_FILE, ONEWIRE_HUB_PORT_CONFIG_VALUE)
			print("-------------------")
			print("Onewire hub reconfigured")
		end
	end
	return nil
end

-- calcDeHumidLevel calucates the RF level for a certain temperature
-- If the RF is higher then returned value, dehumidification shall be done
-- A safety margin of five percent is added
function calcDeHumidLevel(OWFS_temp)
      if (OWFS_temp > 22) then
         return 79 - 5
      else
         humid = integer(-0.0015*OWFS_temp^3+0.1193*OWFS_temp^2-2.9878*OWFS_temp+102.96) - 5
         if (humid > 100) then
            humid = 100
         end
         return humid
      end
end

-- secsToClock return a string representing seconds as HH:MM:SS
function secsToClock(seconds)
   if ((seconds == 0) or (seconds == nil) ) then
      return "00:00:00"
   else
      days  = integer(seconds / (60 * 60 * 24))
      hours = integer((seconds - days * 60 * 60 * 24) / (60 * 60))
      mins  = integer((seconds - days * 60 * 60 * 24 - hours * 60 * 60) / 60)
      secs  = seconds - days * 60 * 60 * 24 - hours * 60 * 60 - mins * 60;

      if (days > 99) then
         return string.format("%03d:%02d:%02d:%02d (DDD:HH:MM:SS)", days, hours, mins, secs)
      elseif (days > 0) then
         return string.format("%02d:%02d:%02d:%02d (DD:HH:MM:SS)", days, hours, mins, secs)
      else
         return string.format("%02d:%02d:%02d (HH:MM:SS)", hours, mins, secs)
      end
   end
end

-- Implode a string into a table using delimiter
function ImplodeTable(delimiter, list)
  local len = #list
  if len == 0 then
    return ""
  end
  local string = list[1]
  for i = 2, len do
    string = string .. delimiter .. list[i]
  end
  return string
end

-- Explode a table content into a string, separate fields with a delimiter
function ExplodeTable(delimiter, text, numElements)
   local list = {}; local pos = 1
   local el = 0
   while (el < numElements) do
      el = el + 1
      local first, last = string.find(text, delimiter, pos)
      if (first) then
         table.insert(list, string.sub(text, pos, first-1))
         pos = last+1
      else
         table.insert(list, string.sub(text, pos))
      break
      end
   end
   return list
end

-- Caculate the mean value of a table
function CalcMeanOnTable(t)
   local sum = 0
   local count= 0
   for k,v in pairs(t) do
      if (type(v) ~= 'number') then
         v = tonumber(v)
      end
      if (type(v) == 'number') then
         sum = sum + v
         count = count + 1
      end
  end
  return (sum/count)
end

-- Calculate the standard deviation of a table
function CalcStandardDeviationOnTable(t)
   local m
   local vm
   local sum = 0
   local count = 0
   local result

   m = CalcMeanOnTable(t)

   for k,v in pairs(t) do
      if (type(v) ~= 'number') then
         v = tonumber(v)
      end
      if (type(v) == 'number') then
         vm = v - m
         sum = sum + (vm * vm)
         count = count + 1
      end
   end

   return math.sqrt(sum / (count-1))
end


-- Calulates values from OWFS taking care of spikes in OWFS values.
-- Using a file as a buffer and calculation of mean and standard deviation.
-- If the value to be inserted deviates more than the standard deviation from mean value,
-- the standard deviation from the mean value in the buffer is returned instead of the actual value.
-- The actual value is stored in the buffer

function getValue(device, value)
   local cache, arr, mean, deviation, allowedDeviation, newValue, elementsInBuffer

   -- Read history from cached file content
   cache = ReadFile(string.format("%s/%s", OWFS_CACHEPATH, device))

   -- If no file found
   if (cache == nil) then
      cache = ";"
   end

   -- Explode the file content into array elements
   arr = ExplodeTable(";", cache, OWFS_BUFFERSIZE)

   -- Calculate
   deviation = tonumber(CalcStandardDeviationOnTable(arr))
   mean      = tonumber(CalcMeanOnTable(arr))

   -- The allowed deviation shall not be too small
   if (deviation < 0.75) then
      allowedDeviation = 0.75
   else
      allowedDeviation = deviation
   end

   -- Limit the changes in the buffer
   if (value > (mean + allowedDeviation)) then
      newValue = mean + allowedDeviation
   elseif (value < (mean - allowedDeviation)) then
      newValue = mean - allowedDeviation
   else
      newValue = value
   end

   -- Insert calulated value last in buffer
   table.insert(arr, newValue)

   elementsInBuffer = # arr

   -- Remove values in front of buffer if buffer size is too big
   if ((elementsInBuffer) > OWFS_BUFFERSIZE) then
      table.remove(arr,1)
      elementsInBuffer = elementsInBuffer - 1
   end

   -- Write the array into file
   WriteFile(string.format("%s/%s", OWFS_CACHEPATH, device), ImplodeTable(";", arr))

   return elementsInBuffer, device, deviation, mean, tonumber(CalcMeanOnTable(arr))
end

-- END OF FUNCTIONS --

commandArray = {}

i = 0
for OWFS_device, name in pairs(OWFS_DEVICES) do

   -- Read temp and humidity from OWFS
   OWFS_temp = ReadOWFS(OWFS_device, "temperature")
   OWFS_hum  = ReadOWFS(OWFS_device, "humidity")

   if ((OWFS_temp ~= nil) and (OWFS_hum ~= nil)) then

      OWFS_temp = tonumber(OWFS_temp)
      OWFS_hum  = tonumber(OWFS_hum)

      -- Correct OWFS value
      bufferSize, device, deviationTemp, meanTemp, tempValue = getValue(string.format("%s_%s", name, "temp"), OWFS_temp)

      print(string.format("%s : Bufsize = %3d, Deviation = %-3.2f, Mean = %-3.2f, OWFS =  %-3.2f, Real = %-3.2f, Diff = %-3.2f", device, bufferSize, deviationTemp, meanTemp, OWFS_temp, tempValue, math.abs(OWFS_temp - tempValue)))

      -- Correct OWFS value
      bufferSize, device, deviationHum, meanHum, humValue = getValue(string.format("%s_%s", name, "hum"), OWFS_hum)

      print(string.format("%s : Bufsize = %3d, Deviation = %-3.2f, Mean = %-3.2f, OWFS =  %-3.2f, Real = %-3.2f, Diff = %-3.2f", device, bufferSize, deviationHum, meanHum, OWFS_hum, humValue, math.abs(OWFS_hum - humValue)))

      -- Calculate the wet level
      startDehumidify = calcDeHumidLevel(tempValue)
      if (humValue > startDehumidify) then
         wet = 3
      elseif (humValue > startDehumidify * 0.8) then
         wet = 0
      else
         wet = 2
      end

      -- Insert values into Domoticz device
      commandArray[i] = { ['UpdateDevice'] = string.format("%s|0|%3.2f;%3.2f;%d", otherdevices_idx[name], tempValue, humValue, wet) }
      i = i + 1
   else
      print(string.format("No OWFS readings from device %s, %s: %s:%s", name, OWFS_device, OWFS_temp, OWFS_hum))
   end
end

return commandArray
Inlägget uppdaterat 2016-06-30 med förtydling och förenklat skript
Paspberry PI, Domoticz, RFXtrx433, Razberry, onewire och en del LUA-skript