Add server-side rendered setup UI accessible via `heatwave web`. The dashboard is now re-rendered per request and includes a nav bar linking to the new /setup page. Setup provides full CRUD for profiles, rooms, devices, occupants, AC units (with room assignment), scenario toggles, and forecast fetching — all via POST/redirect/GET forms. - Add ShowNav field to DashboardData for conditional nav bar - Extract fetchForecastForProfile() for reuse by web handler - Create setup.html.tmpl with Tailwind-styled entity sections - Create web_handlers.go with 15 route handlers and flash cookies - Switch web.go from pre-rendered to per-request dashboard rendering - Graceful dashboard fallback when no forecast data exists
104 lines
3.1 KiB
Go
104 lines
3.1 KiB
Go
package heat
|
||
|
||
// SolarParams holds inputs for simplified solar gain calculation.
|
||
type SolarParams struct {
|
||
AreaSqm float64 // room floor area in m²
|
||
WindowFraction float64 // fraction of wall area that is window (e.g., 0.15)
|
||
SHGC float64 // solar heat gain coefficient of glazing (e.g., 0.6)
|
||
ShadingFactor float64 // 0.0 (fully shaded) to 1.0 (no shading)
|
||
OrientationFactor float64 // 0.0–1.0, varies by hour and wall orientation
|
||
CloudFactor float64 // 0.0 (overcast) to 1.0 (clear)
|
||
SunshineFraction float64 // fraction of hour with sunshine (0.0–1.0)
|
||
PeakIrradiance float64 // W/m² on the surface (e.g., 800 for direct sun)
|
||
}
|
||
|
||
// OrientationFactor returns a solar exposure factor (0.0–1.0) based on
|
||
// wall orientation and hour of day. This is a simplified proxy.
|
||
func OrientationFactor(orientation string, hour int) float64 {
|
||
switch orientation {
|
||
case "S":
|
||
if hour >= 10 && hour <= 16 {
|
||
return 1.0
|
||
}
|
||
if hour >= 8 && hour < 10 || hour > 16 && hour <= 18 {
|
||
return 0.5
|
||
}
|
||
return 0.0
|
||
case "E":
|
||
if hour >= 6 && hour <= 11 {
|
||
return 0.9
|
||
}
|
||
if hour > 11 && hour <= 14 {
|
||
return 0.3
|
||
}
|
||
return 0.0
|
||
case "W":
|
||
if hour >= 14 && hour <= 20 {
|
||
return 0.9
|
||
}
|
||
if hour >= 11 && hour < 14 {
|
||
return 0.3
|
||
}
|
||
return 0.0
|
||
case "SE":
|
||
if hour >= 7 && hour <= 13 {
|
||
return 0.9
|
||
}
|
||
if hour > 13 && hour <= 16 {
|
||
return 0.4
|
||
}
|
||
return 0.0
|
||
case "SW":
|
||
if hour >= 12 && hour <= 19 {
|
||
return 0.9
|
||
}
|
||
if hour >= 9 && hour < 12 {
|
||
return 0.4
|
||
}
|
||
return 0.0
|
||
case "NE":
|
||
if hour >= 5 && hour <= 9 {
|
||
return 0.5
|
||
}
|
||
return 0.1
|
||
case "NW":
|
||
if hour >= 17 && hour <= 21 {
|
||
return 0.5
|
||
}
|
||
return 0.1
|
||
case "N":
|
||
return 0.1 // minimal direct sun
|
||
default:
|
||
return 0.5
|
||
}
|
||
}
|
||
|
||
// SolarGain returns estimated solar heat gain in watts.
|
||
// Formula: irradiance * orientationFactor * windowArea * SHGC * shadingFactor * cloudFactor * sunshineFraction
|
||
func SolarGain(p SolarParams) float64 {
|
||
windowArea := p.AreaSqm * p.WindowFraction
|
||
return p.PeakIrradiance * p.OrientationFactor * windowArea * p.SHGC * p.ShadingFactor * p.CloudFactor * p.SunshineFraction
|
||
}
|
||
|
||
// DefaultRhoCp is the volumetric heat capacity of air in J/(m³·K).
|
||
// Approximately 1200 J/(m³·K) at sea level.
|
||
const DefaultRhoCp = 1200.0
|
||
|
||
// VentilationParams holds inputs for ventilation heat gain/loss calculation.
|
||
type VentilationParams struct {
|
||
ACH float64 // air changes per hour
|
||
VolumeCubicM float64 // room volume in m³
|
||
OutdoorTempC float64 // outdoor temperature in °C
|
||
IndoorTempC float64 // indoor temperature in °C
|
||
RhoCp float64 // volumetric heat capacity in kJ/(m³·K); use value such that kJ * 1000 = J, or pass as J directly
|
||
}
|
||
|
||
// VentilationGain returns ventilation heat gain in watts.
|
||
// Positive = heating (outdoor hotter), negative = cooling (outdoor cooler).
|
||
// Formula: ACH * volume * rhoCp_J * deltaT / 3600
|
||
func VentilationGain(p VentilationParams) float64 {
|
||
rhoCpJ := p.RhoCp * 1000 // convert kJ/(m³·K) to J/(m³·K)
|
||
deltaT := p.OutdoorTempC - p.IndoorTempC
|
||
return p.ACH * p.VolumeCubicM * rhoCpJ * deltaT / 3600
|
||
}
|