print("-----OWFS TIME-----") -- OWFS path OWFS_PATH = "/mnt/1wire/" OWFS_TEMP_HUM_DEVICES = { ['26.160592010000'] = 'Krypgrund_SO', ['26.740592010000'] = 'Krypgrund_SV', ['26.802692010000'] = 'Krypgrund_NO', ['26.1851E2010000'] = 'Ute' } OWFS_TEMP_DEVICES = { ['28.1C3D99030000'] = "Avfuktare_Tilluft_Temp2", ['28.87917A060000'] = "Avfuktare_Avluft_Temp2", ['28.A9807B060000'] = "Avfuktare_Processluft_Temp2", ['28.DC2E7A060000'] = "Avfuktare_Uteluft_Temp2" } -- 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, UseCache) if (UseCache == false) then path = OWFS_PATH.."uncached/" else path = OWFS_PATH end value = ReadFile(string.format("%s/%s/%s", 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(temp) local humidityThreshold if (temp < 0) then humidityThreshold = 100 elseif (temp > 22) then humidityThreshold = 79 - 5 else humidityThreshold = integer(-0.0015 * temp ^ 3+0.1193 * temp ^ 2 - 2.9878 * temp + 102.96) - 5 if (humidityThreshold > 100) then humidityThreshold = 100 elseif (humidityThreshold < 0) then humidityThreshold = 0 end end return humidityThreshold 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 -- Get 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 -- Get 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 an file 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 from the buffer is returned instead of the actual value. -- The actual value is stored in 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, deviation, mean, tonumber(CalcMeanOnTable(arr)) end -- END OF FUNCTIONS -- commandArray = {} i = 0 for OWFS_device, name in pairs(OWFS_TEMP_HUM_DEVICES) do -- Read temp and humidity from OWFS OWFS_temp = ReadOWFS(OWFS_device, "temperature", false) OWFS_hum = ReadOWFS(OWFS_device, "humidity", false) if ((OWFS_temp ~= nil) and (OWFS_hum ~= nil)) then OWFS_temp = tonumber(OWFS_temp) OWFS_hum = tonumber(OWFS_hum) -- Correct OWFS value bufferSize, deviationTemp, meanTemp, tempValue = getValue(string.format("%s_%s", name, "temp"), OWFS_temp) print(string.format("%s:temp Bufsize = %3d, Deviation = %-3.2f, Mean = %-3.2f, OWFS = %-3.2f, Used = %-3.2f, Diff = %-3.2f", name, bufferSize, deviationTemp, meanTemp, OWFS_temp, tempValue, math.abs(OWFS_temp - tempValue))) -- Correct OWFS value bufferSize, deviationHum, meanHum, humValue = getValue(string.format("%s_%s", name, "hum"), OWFS_hum) print(string.format("%s:humi Bufsize = %3d, Deviation = %-3.2f, Mean = %-3.2f, OWFS = %-3.2f, Used = %-3.2f, Diff = %-3.2f", name, 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 if (otherdevices_idx[name] ~= nil) then 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("Cannot find id number for device %s", name)) end else print(string.format("No OWFS readings from device %s, %s: %s:%s", name, OWFS_device, OWFS_temp, OWFS_hum)) end end for OWFS_device, name in pairs(OWFS_TEMP_DEVICES) do -- Read temp and humidity from OWFS OWFS_temp = ReadOWFS(OWFS_device, "temperature", true) if (OWFS_temp ~= nil) then OWFS_temp = tonumber(OWFS_temp) print(string.format("%s: temp = %3.2f", name, OWFS_temp)) -- Insert values into Domoticz device if (otherdevices_idx[name] ~= nil) then commandArray[i] = { ['UpdateDevice'] = string.format("%s|0|%3.2f", otherdevices_idx[name], OWFS_temp) } i = i + 1 else print(string.format("Cannot find id number for device %s", name)) end else print(string.format("No OWFS readings from device %s, %s: %s:%s", name, OWFS_device, OWFS_temp, OWFS_hum)) end end return commandArray