Skip to content

Floor Heating Operating Cost Calculator - Technical Specification

The WarmlyYours Operating Cost Calculator estimates the daily, monthly, and annual electricity costs for running radiant floor heating systems. The calculator has two implementations:

  1. Public Calculator - A simplified client-side calculation for quick estimates
  2. Advanced Calculator - A backend service that accounts for monthly temperature variations and thermostat schedules

  • ViewComponent: app/components/www/floor_heating_calculator_component.rb
  • ViewComponent: app/components/www/snow_melting_calculator_component.rb
  • Stimulus controller: app/javascript/controllers/floor_heating_calculator_controller.js
  • Stimulus controller: app/javascript/controllers/snow_melting_calculator_controller.js
Cost (cents/day) = kWh_rate × sqft × (hours / 2.3) × wattage / 1000
VariableDescriptionDefaultSource
kWh_rateElectricity rate (cents/kWh)12-13¢Fetched from /api/v1/locations/by_postal_code/:zip
sqftHeated square footage35 sq ftUser input
hoursDaily operating hours8 hoursUser input (slider)
wattageWatts per square foot15 W/sq ftBased on selected heating system

The divisor 2.3 represents a duty cycle factor, accounting for the fact that floor heating systems cycle on and off rather than running continuously. This equates to approximately 43% runtime (1/2.3 ≈ 0.43).

Heating SystemWatts/sq ft
TempZone Flex Roll15
TempZone Easy Mat15
TempZone Cable (3” spacing)14.8
TempZone Cable (4” spacing)11.1
TempZone Cable (5” spacing)8.9
Environ Flex Roll12
Environ Easy Mat12
Slab Heat Mat20

For a 50 sq ft bathroom with TempZone Flex Roll, 8 hours/day use, at 12¢/kWh:

Cost = 12 × 50 × (8 / 2.3) × 15 / 1000
= 12 × 50 × 3.48 × 15 / 1000
= 31,320 / 1000
= 31.32 cents/day (~$0.31/day)

  • app/services/room_configuration/operating_costs_calculator.rb
  • config/initializers/room_configuration_constant.rb
  • app/models/room_configuration.rb

The advanced calculator computes monthly costs by:

  1. Fetching local average monthly temperatures by postal code
  2. Calculating operating hours based on temperature differential
  3. Applying weekday/weekend thermostat schedules
  4. Multiplying by system wattage and electricity rate
monthly_cost = operating_hours × kilowatts × electricity_rate

Where:

operating_hours = (comfort_hours × comfort_factor) + (economy_hours × economy_factor)

The system calculates how much heating is needed based on the temperature differential between desired indoor temperature and actual outdoor temperature:

# Comfort mode factor
tdiff_comfort = comfort_temp - outdoor_temp
tdiff_comfort = 0 if tdiff_comfort < 0 # No heating needed if warmer outside
tdiff_comfort = comfort_temp if tdiff_comfort > comfort_temp # Cap at max
factor_comfort = tdiff_comfort / comfort_temp
# Economy mode factor
tdiff_economy = economy_temp - outdoor_temp
tdiff_economy = 0 if tdiff_economy < 0
tdiff_economy = economy_temp if tdiff_economy > economy_temp
factor_economy = tdiff_economy / economy_temp

Interpretation:

  • When outdoor temp equals indoor setpoint → factor = 0 (no heating needed)
  • When outdoor temp is very cold → factor approaches 1.0 (maximum heating)
  • Linear interpolation between these extremes

Defined in config/initializers/room_configuration_constant.rb:

module RoomConfigurationConstants
AVG_DAYS_PER_MONTH = {
week_day: 21.75,
week_end_day: 8.69
}
HOURS_OF_OPERATION_PER_DAY = {
comfort: { week_day: 9.0, week_end_day: 15.0 },
economy: { week_day: 15.0, week_end_day: 9.0 }
}
AVERAGE_HOURS = {
avg_num_hrs_per_month: 730.56,
avg_num_comfort_hrs_per_month: 332.605, # ~11 hrs/day
avg_num_economy_hrs_per_month: 397.955 # ~13 hrs/day
}
end

Defined in app/models/room_configuration.rb:

TEMPERATURE_OPTIONS = [
['Low', 60.0], # Economy setting
['Average', 68.0], # Comfort setting (default)
['Warm', 72.0],
['Hot', 80.0]
]
def calculate_total_watts
heating_elements.sum { |he| he.quantity * he.item.watts.to_f }
end
def calculate_total_kilowatts
calculate_total_watts / 1000.0
end
DataSourceModel
Average monthly temperaturesBy postal codeAverageMonthlyTemperature
Electricity ratesBy postal codeElectricityRate
Heating element wattageProduct catalogItem

Electricity Rate Philosophy (Updated December 2025)

Section titled “Electricity Rate Philosophy (Updated December 2025)”

For provinces/states with tiered pricing structures, we use the marginal rate (Tier 2 / Step 2) rather than the blended average. This is because:

  1. Floor heating is additive consumption - When customers add floor heating, they’re adding NEW load on top of existing usage
  2. Base usage consumes lower tiers - A household’s existing consumption (lights, appliances, HVAC) typically uses up the cheaper Tier 1 allowance
  3. Additional heating pays the higher rate - The floor heating electricity is billed at the marginal (higher) rate

Example - Quebec (Hydro-Québec Rate D):

  • Tier 1: ~6.9¢/kWh (first 40 kWh/day)
  • Tier 2: ~10.7¢/kWh (above 40 kWh/day) ← We use this rate
  • Using the blended average (7.8¢) would underestimate heating costs by ~27%

Provinces with tiered pricing:

  • Quebec: Tier 2 rate (10.7¢) used instead of blended average
  • BC: Step 2 rate (14.08¢) used instead of Step 1 (11.72¢)
  • Ontario: Mid-peak TOU rate used as representative for heating load

When detailed room information is available, the calculator uses heat loss calculations for more accurate estimates.

Heat loss calculation is used when:

  • allow_heat_loss_calculation? returns true
  • can_calculate_heat_loss? returns true
  • Room has an indoor floor type

The detailed calculation accounts for:

  • Room insulation (walls, ceiling, floor)
  • Window and door openings (U-values)
  • Infiltration/air sealing quality
  • Room dimensions and orientation
BTU_PER_HOUR_PER_WATT = 3.4121416

Located in app/models/heat_lossable_constants.rb

def self.get_monthly_operation_kwh(hl_avg_comfort, hl_avg_economy)
monthly_kwh = 0.0
[:week_day, :week_end_day].each do |days_type|
{ comfort: hl_avg_comfort, economy: hl_avg_economy }.each do |mode, hl|
monthly_kwh += AVG_DAYS_PER_MONTH[days_type] *
HOURS_OF_OPERATION_PER_DAY[mode][days_type] *
(hl / BTU_PER_HOUR_PER_WATT) / 1000.0
end
end
monthly_kwh
end

GET /api/v1/locations/by_postal_code/:postal_code

Response:

{
"status": "ok",
"average_rate": 0.12
}
GET /api/v1/locations/by_lat_lng/?lat=:latitude&lng=:longitude

{
status: 'ok',
operating_cost_by_month: [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec],
operating_cost_annual: 125.50,
operating_cost_by_month_average: 10.46,
operating_cost_by_coldest_month: 28.75,
message: "assuming default SmartStat settings, yielding average operation of
4.2 hours per day annually, and coldest month operation of
8.1 hours per day, based on average monthly temperatures and
electricity rates in your area."
}

The frontend converts the cost to display:

  • Per day: ${(cost / 100).toFixed(2)}
  • Per month: ${((cost / 100) * 30).toFixed(2)}
  • Per year: ${((cost / 100) * 365).toFixed(2)}

Based on the website copy (app/views/pages/floor-heating.html.erb):

Electric floor heating typically costs $0.07–$0.36 per hour. For a standard bathroom (~40 sq. ft.) running a few hours daily: $17–$29 per month.

  1. Room size — Larger rooms = higher costs
  2. Flooring material — Tile/stone transfer heat efficiently; carpet/wood may require more energy
  3. Daily usage — 2 hours vs 6 hours makes a significant difference
  4. Electricity rates — Local kWh pricing directly impacts costs

FilePurpose
app/components/www/floor_heating_calculator_component.rbFloor heating calculator ViewComponent
app/components/www/snow_melting_calculator_component.rbSnow melting calculator ViewComponent
app/javascript/controllers/floor_heating_calculator_controller.jsStimulus controller for floor heating
app/javascript/controllers/snow_melting_calculator_controller.jsStimulus controller for snow melting
app/services/room_configuration/operating_costs_calculator.rbBackend calculator service
config/initializers/room_configuration_constant.rbConstants for hours/days
app/models/room_configuration.rbTemperature options, wattage tables
app/models/heat_lossable_constants.rbBTU conversion constant
app/views/pages/floor-heating/cost-calculator.html.erbPublic calculator page