Automated Fan Speed

Hi all, I’m getting acquainted with my vera in our new home. Lua doesn’t intimidate me, but I’m having a bit of trouble wrapping my head around the UI and event engine.

I’m going to make my ceiling fan balance the load between upstairs and downstairs heat pump units. It’s located in an open common area, and controlled by a Leviton fan controller. Both heatpumps are on ecobee thermostats and I have a trial code working:

local maxTempDelta = 6;
local minTempDelta = 2;
local fanSetting = 0;
local downTemp = luup.variable_get(“urn:upnp-org:serviceId:TemperatureSensor1”,“CurrentTemperature”,11);
local upTemp = luup.variable_get(“urn:upnp-org:serviceId:TemperatureSensor1”,“CurrentTemperature”,8);

if (math.abs(tonumber(downTemp)-tonumber(upTemp))>minTempDelta)
then
fanSetting = (math.abs(tonumber(downTemp)-tonumber(upTemp))/(maxTempDelta-minTempDelta))*100
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = fanSetting}, 4);
else
luup.call_action(“urn:upnp-org:serviceId:Dimming1”, “SetLoadLevelTarget”, {newLoadlevelTarget = “0”}, 4);
end

This works fine when ran in ‘Test Lua’.

My question has two parts. First, what is the best way to implement the above code, scene or startup lua? If scene, do I just leave all the devices off in the setup and let the Lua code turn on the fan? Are there resource load implications to running this in startup lua?

Second, I would like to add functionality such that when the fan controller is manually changed (a person hits the dimmer on the wall), the autoFan code will be temporarily disabled. I was thinking of using vcontainer to store the user input flag. I think I can handle programming the delay portion, but don’t know how to log a user hitting the switch versus the autoFan code setting the loadLevel.

Any and all help is much appreciated!

P.S. How does one make a code snippet? The button isn’t doing anything. Also tried to no avail.

Thanks

First, what is the best way to implement the above code, scene or startup lua?

You presumably want to run this code periodically. Code in Startup Lua runs only when Vera restarts. You could write it as a function and set up a call_delay(…) to run it periodically. It is simpler to place the code in a scene and create a schedule to run it as often as you want.

If scene, do I just leave all the devices off in the setup and let the Lua code turn on the fan?

Yes. Place the code on the scene LUUP tab and leave everything on the DEVICES tab unchecked.

Are there resource load implications to running this in startup lua?

The resource requirements are the same for Lua in a scene or in Startup Lua. The impact is negligible even if you ran it every minute. I suggest you check whether the calculated level is different from the current level before running the luup.call_action(…). This will minimize unnecessary commands.

Second, I would like to add functionality such that when the fan controller is manually changed (a person hits the dimmer on the wall), the autoFan code will be temporarily disabled.

I answered this in another post.

P.S. How does one make a code snippet? The button isn't doing anything. Also tried to no avail.

After you click the code button (#), paste your code between the tags. You were close but the real code tags use [] rather than <>. If the code button doesn’t do anything, it may be your browser. Switch to using Chrome or Firefox.

Thanks Rex! I’ll try it out.

Alright, almost have it working. I’m using kwikLog to debug, but for some reason as the complexity grew, it stopped writing to the log file.

When I run:

-- Settings
local maxTempDelta = 4;  -- Temperature delta where fan is set to 100%
local minTempDelta = 0;  -- Temperature delta where fan is set to 0%
local delayMin     = 60; -- Time after manually setting fan to resume autoFan

-- Log file function, primarily for debugging
local function kwikLog(message, clear)
	local socket = require("socket")
	local time = socket.gettime() or os.time()
	local tms = string.format(".%03d  ",math.floor (1000 * (time % 1))) 
	local stamp = os.date("%d %b %Y  %T",math.floor(time)) .. tms
	local mode = "a+"
	if clear then mode = "w+" end
	local file = io.open("/www/fanLog.txt", mode)
	file:write(stamp .. (message or "") .. "\n")
	file:close()
end

-- Declarations
local fanLLS       = 0;
local fanLLT       = 0;
local fanLLStime   = 0;
local fanLLTtime   = 0;
local delaySeconds = delayMin*60;

-- Get current fan and thermostat data
fanLLS, fanLLStime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","ALoadLevelStatus",4);
fanLLT, fanLLTtime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelTarget",4);
local downTemp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",11);
local upTemp   = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",8);

-- Log current state
kwikLog("LoadLevelStatus:   "..fanLLS.." "..fanLLStime)
kwikLog("LoadLevelTarget:   "..fanLLT.." "..fanLLTtime)
kwikLog("Up and Down Temp:  "..upTemp.." "..downTemp)

-- If upstairs and downstairs temp isn't balanced, and the fan hasn't been manually set determine fan setting...
if ((math.abs(tonumber(downTemp)-tonumber(upTemp))>minTempDelta) and ((fanLLS==fanLLT) or (fanLLTtime>delaySeconds))
then
  local fanSetting = math.abs(tonumber(downTemp)-tonumber(upTemp))/(maxTempDelta-minTempDelta)
  luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = fanSetting}, 4);
  kwikLog("Fan Set to "..fanSetting.."%")
else 
  local now     = socket.gettime() or os.time()
  local minsAgo = (now - fanLLStime)/60;
  kwikLog("Fan was manually set ".." to "..fanLLS.." "..minsAgo.." minutes ago")
end

kwikLog("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")

It does not write to the log file. Deleting a few segments and running the code below does update the log file. Neither code gives an error in ‘Test Lua’.


-- Settings
local maxTempDelta = 4;
local minTempDelta = 0;

local function kwikLog(message, clear)
	local socket = require("socket")
	local time = socket.gettime() or os.time()
	local tms = string.format(".%03d  ",math.floor (1000 * (time % 1))) 
	local stamp = os.date("%d %b %Y  %T",math.floor(time)) .. tms
	local mode = "a+"
	if clear then mode = "w+" end
	local file = io.open("/www/fanLog.txt", mode)
	file:write(stamp .. (message or "") .. "\n")
	file:close()
end

local fanLLS       = 0;
local fanLLT       = 0;
local fanLLStime   = 0;
local fanLLTtime   = 0;

fanLLS, fanLLStime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelStatus",4);
fanLLT, fanLLTtime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelTarget",4);
kwikLog("LoadLevelStatus:   "..fanLLS.." "..fanLLStime)
kwikLog("LoadLevelTarget:   "..fanLLT.." "..fanLLTtime)

Anyone see the error that ‘Test Luup’ doesn’t catch?

Thanks,
Redneck

You are missing a closing parenthesis on line 38. Also don’t split an if and its then - it can cause problems.

if ((math.abs(tonumber(downTemp)-tonumber(upTemp))>minTempDelta) and ((fanLLS==fanLLT) or (fanLLTtime>delaySeconds))) then

If you want better error messages, have a look at LuaTest.

You are correct sir! Testing now, and I’ll let you know how it goes.

I read into LuaTest, and it sounds promising. Thanks again for your help.

I’m pretty sure it’s working now. Here’s the completed code:

-- Settings
local maxTempDelta = 4;  -- Temperature delta where fan is set to 100%
local minTempDelta = 0;  -- Temperature delta where fan is set to 0%
local delayMin     = 60; -- Time after manually setting fan to resume autoFan

-- Log file function, primarily for debugging
local function kwikLog(message, clear)
	local socket = require("socket")
	local time = socket.gettime() or os.time()
	local tms = string.format(".%03d  ",math.floor (1000 * (time % 1))) 
	local stamp = os.date("%d %b %Y  %T",math.floor(time)) .. tms
	local mode = "a+"
	if clear then mode = "w+" end
	local file = io.open("/www/fanLog.txt", mode)
	file:write(stamp .. (message or "") .. "<br>\n")
	file:close()
end

-- Declarations
local socket = require("socket")
local fanLLS       = 0;
local fanLLT       = 0;
local fanLLStime   = 0;
local fanLLTtime   = 0;

-- Get current fan and thermostat data
fanLLS, fanLLStime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelStatus",4);
fanLLT, fanLLTtime = luup.variable_get("urn:upnp-org:serviceId:Dimming1","LoadLevelTarget",4);
local downTemp = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",11);
local upTemp   = luup.variable_get("urn:upnp-org:serviceId:TemperatureSensor1","CurrentTemperature",8);
local now      = socket.gettime() or os.time()
local minsAgo = (now - fanLLStime)/60;

-- Log current state
kwikLog("LoadLevelStatus:   " .. fanLLS .. " " .. fanLLStime)
kwikLog("LoadLevelTarget:   " .. fanLLT .. " " .. fanLLTtime)
kwikLog("Up and Down Temp:  " .. upTemp .. " " .. downTemp)

-- If fan hasn't been manually set or the manual setting delay time has elapsed determine fan setting...
if ((fanLLS==fanLLT) or (minsAgo>delayMin)) then
  local fanSetting = (math.abs(tonumber(downTemp)-tonumber(upTemp))/(maxTempDelta-minTempDelta))*100
  luup.call_action("urn:upnp-org:serviceId:Dimming1", "SetLoadLevelTarget", {newLoadlevelTarget = fanSetting}, 4);
  kwikLog("Fan Set to "..fanSetting.."%")
else
  kwikLog("Fan was manually set ".." to "..fanLLS.." "..minsAgo.." minutes ago")
end

kwikLog("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")