Time-of-Use Air Conditioning With a Vera Smart Home Controller
by Unresto in Workshop > Home Improvement
4570 Views, 74 Favorites, 0 Comments
Time-of-Use Air Conditioning With a Vera Smart Home Controller
This Instructable will show how to run your home HVAC less often during "Time-of-Use" peak hours and potentially cut power bills in half on extreme days. I implemented this using a VeraLite smart home controller and tested with both Z-Wave and Nest thermostats.
How does it work? Just imagine during peak hours repeatedly switching your thermostat to "Cool" for 5 minutes then to "Off" for 10 minutes. That's all your HVAC needs anyway on a moderate day but on very hot days it's not enough. That means you will get regular bursts of cool air but your inside temperature will start to slowly rise. A little less comfortable with a fraction of the cost.
This is automated by coding a Vera scene to run every minute and switch between modes. Scenes are written in Vera's Luup (Lua-UPnP) language and can be triggered to run based on timers or events. It works for both heating and cooling and allows customization based on your power company's time-of-use plan, which may have different costs based on time of day, day of week, or month.
REQUIREMENTS
You will need a working knowledge of coding, create and running Vera scenes, and debugging when things go wrong. I provide basic working code examples but you must modify them for your application. To get started you'll need:
- Details of your power company's "Time-of-Use" plan
- Vera smart home controller, like the VeraLite ($99)
- Any Vera compatible thermostat - I have specifically tested this on:
- 2gig CT100 Z-Wave Programmable Thermostat ($70)
- Various Trane Z-Wave Thermostats ($100 and up)
- Nest Thermostat plus the Vera Plugin for Nest ($249)
It would be straightforward to modify these concepts to SmartThings and other controllers. Feel free to copy, share, and create your own Instructable to improve upon these ideas.
MOTIVATION
We received a Nest Thermostat as a wedding present (awesome friends, right?) and it's a cool gadget but it really hasn't saved much money. Someone is home almost all the time so Auto-Away rarely kicks in, and hot California summers either keep the AC running all the time - or not at all if you set it too high. With Pacific Gas & Electric with rates ranging from $0.10 to $0.50 per kWh that can be $30 a day. Ouch!
As you can see from the picture above I have been able to flatten those costs out during peak hours. In spite of the high temperature of 107F on Wednesday July 29th 2015 the daily cost was about $12.50.
Time-of-Use Power Plan
WARNING: Proceed with caution, you can easily mess this up and end up paying more. Learn from my mistakes!
Call your power company or get on their website and find out what's offered. I'm in rural California so with PG&E I found:
- The basic plan options: www.pge.com/rateoptions
- A list of "off peak" holidays: http://www.pge.com/tariffs/toudates.shtml
- A spreadsheet of all details: http://www.pge.com/tariffs/ResTOUCurrent.xls
In my case I read through this info and found the PG&E "E-6" plan was available to me and fit within what I was trying today. It has different schedules for summer vs. winter, weekdays vs. weekends and holidays, and three different rates: Off Peak, Partial Peak, and Full Peak.
PITFALL #1
You might have to wait a full billing cycle to change to a Time-of-Use plan - and a full billing cycle to change back! If you find out that you're using more power than expected during peak hours you could end up with a hefty bill.
PITFALL #2
Even though you've selected a Time-of-Use plan you may still have Tiered pricing that applies. For example with the PG&E "E-6" plan the peak price looks to be about $0.30/kWh - but during a billing cycle after you consume some amount of kWh at that rate the price ratchets up 50%. You will have to factor that into your cost savings calculations later on.
PITFALL #3
Don't be too aggressive with your power plan until you understand exactly how it works. I originally picked the PG&E "E-6+SmartRate" plan that promised 20% better savings than "E-6" - with the restriction that between 9 and 15 days a year the peak power would have an additional surcharge
The very first day of my plan was 105 degrees and I got an email that I would be charged $0.60 additional for each Full Peak kWh. I would have to turn the AC completely off to save any money. Luckily I only had to wait a day to turn the "SmartRate" option off.
Vera Scene - Initialize Device IDs
To start creating the Vera scene code you will need to know the device ID for the thermostat you want to control. The screenshot shows my Nest thermostat using Vera firmware UI7, which is slightly different than earlier versions. In all versions simply click on a device's details (>) and look in the "Advanced" tab to find the device ID. In my case the thermostat is device #39.
You can also add any number of thermostats or other Z-Wave switches to toggle during peak hours. In my setup power to the attic fan is controlled by device #63.
Open up a text editor and start writing your scene code:
local id_hvac1 = 39 local id_switch1 = 63
OPTION: Using a Nest Thermostat
If you want to use a Nest Thermostat install and configure the Vera Nest Plugin. However, unlike Z-Wave thermostats which are locally sent over Z-Wave, Nest commands require an internet connection - and may occasionally fail to work reliably. The Nest could fail to receive commands and may will cost you extra during peak hours.
Vera Scene - Time-of-Use Targets
You will now need to decide how much you want your HVAC to run during each time-of-use period. The scene will run one at one minute intervals to check the time-of-use policy based on these settings:
local tou_ticks = 15 -- Ticks per cycle, must be <60 minutes local tou_fullpeak = {t="FullPeak", l=5} local tou_partpeak = {t="PartPeak", l=8} local tou_cooldown = {t="CoolDown", l=10} local tou_holiday = {t="Holiday", l=tou_ticks} local tou_offpeak = {t="OffPeak", l=tou_ticks}
For example, I decided to use a 15 minute cycle and during "FullPeak" the HVAC can only run 5 minutes - or 33% of the time. I don't recommend running less than 5 minutes at a time as this can put undue stress on your HVAC system.
COOL DOWN
The "FullPeak", "PartialPeak", "OffPeak", and "Holiday" correspond to the power company's time-of-use policy. But what about "CoolDown"?
Through experimentation I found on very hot days as we went peak to off-peak the air conditioner would run for almost 3 hours straight as it tried to cool down a warmer than normal house. This became almost uncomfortably cold.
When I noticed the kids wrapped up in blankets on summer evenings I realized I could improve this.
During "CoolDown" I continue to artificially limit the HVAC even after PG&E rates have dropped to the lowest. The outside air temperature will slowly cool down, and I just elect to have the HVAC not work as hard to try and get back on track. This only has any meaningful difference on extreme temperature days.
Vera Scene - Time of Use Policy Definition
The next bit of code defines the time-of-use policy based on time and date. Summer and winter have different rules for full and partial peak, and weekends are off peak. Note that to simplify the tables some entries overlap - for example summer (months 5-10) are checked first, then winter next (months 1-12). Same for weekdays vs. weekends, and bookending partial peak around full peak on summer weekdays.
local tou_periods = { {toy="Summer", fmonth=5, lmonth=10, {tow="Weekday", fwday=2, lwday=6, {b=13, e=19, p=tou_fullpeak}, {b=10, e=21, p=tou_partpeak}, {b=21, e=24, p=tou_cooldown}}, {tow="Weekend", fwday=1, lwday=7, {b=17, e=20, p=tou_partpeak}, {b=20, e=23, p=tou_cooldown}}}, {toy="Winter", fmonth=1, lmonth=12, {tow="Weekday", fwday=2, lwday=6, {b=17, e=20, p=tou_partpeak}, {b=20, e=23, p=tou_cooldown}}}}
Holidays vary from year to year so this code will need maintenance once a year. If you like you can ignore this section and do more aggressive limiting on holiday days:
-- 2015 Holidays - 1/1, 2/16, 5/25, 7/4, 9/7, 11/11, 11/26, 12/25 local tou_holidays = { {m=1,d=1}, {m=2,d=16}, {m=5,d=25}, {m=7,d=4}, {m=9,d=7}, {m=11,d=11}, {m=11,d=26}, {m=12,d=25}}
Vera Scene - Find Policy Function
Now here's a function that takes the current date/time plus Lua tables for the holidays and periods and finds the match, or "OffPeak" if none is found. I coded this as a function because future extensions (coming soon!) will be calling this from multiple places:
function tou_find_policy(dt, holidays, periods) -- Holidays by month/date for ihol,hol in ipairs(holidays) do if (dt.month == hol.m) and (dt.day == hol.d) then return tou_holiday end end -- Periods by time-of-year/time-of-week/time-of-day for itoy,toy in ipairs(periods) do if (dt.month >= toy.fmonth) and (dt.month <= toy.lmonth) then for itow,tow in ipairs(toy) do if (dt.wday >= tow.fwday) and (dt.wday <= tow.lwday) then for itod,tod in ipairs(tow) do if (dt.hour >= tod.b) and (dt.hour < tod.e) then return tod.p end end end end end end return tou_offpeak end
Vera Scene - HVAC Tick Function
For each time this scene is called we call the tou_hvac_tick() function to toggle thermostat modes and switches based on current tick versus ticks allowed per the time-of-use policy. If you have multiple thermostats you can call this function once for each. To start we define parameters and look at the current mode (Off/Cool/Heat/Auto) and state (Idle, Cooling, Heating) of the thermostat:
function tou_hvac_tick(tick, policy, dt, id) local urn_hvac_mode = "urn:upnp-org:serviceId:HVAC_UserOperatingMode1" local urn_hvac_state = "urn:micasaverde-com:serviceId:HVAC_OperatingState1" local mode_status = luup.variable_get(urn_hvac_mode, "ModeStatus", id) local mode_state = luup.variable_get(urn_hvac_state, "ModeState", id)
To make your thermostat work as normally as possible it earns a credit when on but idle and loses a credit otherwise. If the thermostat has credits then it isn't limited, meaning on a moderate day you won't see the thermostat constantly cycling between off and active. If it does start cooling or heating it will do so until it runs out of credits, meaning if you arrive home and the house is too hot you can crank down the temperature and give it a boost without immediately starting limiting.
-- TimeOfUseCredits: Increment if thermostat on but idle; decrement if active local var_credits = tonumber(luup.variable_get("TimeOfUse", "TimeOfUseCredits", id) or 0) if (mode_status ~= "Off") then if (mode_state == "Idle") and (var_credits < policy.l) then var_credits = var_credits + 1 elseif (mode_state ~= "Idle") and (var_credits > 0) then var_credits = var_credits - 1 end end
The difficult part here is dealing with occasional failure to transmit Z-Wave or Nest thermostat mode change requests. When switching a thermostat off we record the current mode as the "TimeOfUsePrevious" variable of the thermostat device. Then when resuming we set "ModeTarget" to this value and on the next tick verify "ModeStatus" to see that the command took. Only then do we clear the previous mode to "None".
-- TimeOfUsePrevious: Set "Off" if active or previous mode when limited local var_previous = luup.variable_get("TimeOfUse", "TimeOfUsePrevious", id) or "None" if (tick >= policy.l) and (var_credits == 0) then if (var_previous == "None") then luup.variable_set("TimeOfUse", "TimeOfUsePrevious", mode_status, id) luup.call_action(urn_hvac_mode, "SetModeTarget", {NewModeTarget = "Off"}, id) end elseif (var_previous ~= "None") then if (var_previous == mode_status) then luup.variable_set("TimeOfUse", "TimeOfUsePrevious", "None", id) end luup.call_action(urn_hvac_mode, "SetModeTarget", {NewModeTarget = var_previous}, id) end
Lastly save the new credit count. For debugging purposes I also log the time of use policy as a variable on the thermostat device and end the function:
luup.variable_set("TimeOfUse", "TimeOfUseCredits", var_credits, id) luup.variable_set("TimeOfUse", "TimeOfUsePolicy", policy.t, id) end
Vera Scene - Switch Tick Function
If you want to limit normal Z-wave switches code up this function and call once for each switch device ID:
function tou_switch_tick(tick, policy, id) local urn_switch = "urn:upnp-org:serviceId:SwitchPower1" local switch_target = 1 if (tick > policy.l) then switch_target = 0 end luup.call_action(urn_switch , "SetTarget", {newTargetValue = switch_target}, id) end
Vera Scene - Processing Devices
And last but not least, call the functions we defined to get the current policy and process ticks for HVAC and switch devices in your system:
local curr_time = os.date('*t') local curr_tick = ((curr_time.hour*60) + curr_time.min) % tou_ticks local curr_policy = tou_find_policy(curr_time, tou_holidays, tou_periods) -- HVAC tou_hvac_tick(curr_tick, curr_policy, curr_time, id_hvac1) -- SWITCH tou_switch_tick(curr_tick, curr_policy, id_switch1) return true
Triggering the Scene
The last step is to create a Vera scene and schedule it to trigger every 1 minute and have it execute the Luup scene code we created. I've attached the complete sample code here for you convenience, which you will need to modify for your device IDs and your power company's time-of-use plan.
Here I show a Wattvision graph of my alternating power usage during this last weekend's partial-peak time, then transition to off-peak at 8PM. If you want to see graphs like this yourself your power company may support reading your meter directly, such as the PG&E smart meter integration with the Rainforest Eagle.
Now sit back and enjoy the savings! You may need to tweak your settings to your specific house and HVAC as you go. It took me about a week's worth of experiments to dial mine in. Good luck and feel free to ask if any questions.
Downloads
CHEATING!
So now you're saving half on your energy bill each month, but you're sitting at home one hot day and you just can't bear it - you HAVE to have some more of that cool air. Just create yourself a new a scene that adds a few credits:
local id_hvac = 39 local var_credits = tonumber(luup.variable_get("TimeOfUse", "TimeOfUseCredits", id_hvac) or 0) var_credits = var_credits + 5 luup.variable_set("TimeOfUse", "TimeOfUseCredits", var_credits, id_hvac)return true
Use with discretion!