Fixa spikar i avläsning med Lua (Domoticz) från onewire
Postat: 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.
Inlägget uppdaterat 2016-06-30 med förtydling och förenklat skript
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