From 28479e3ca12098d606d930d1f4edcb1980ca4b40 Mon Sep 17 00:00:00 2001 From: Michael Anderjaska Date: Wed, 23 Aug 2023 15:37:55 -0700 Subject: [PATCH] moved to seperate pr --- src/lib/util/thermostats.ts | 140 ++++++++++++++++++ .../climate_setting_schedules/create.ts | 3 +- 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/lib/util/thermostats.ts diff --git a/src/lib/util/thermostats.ts b/src/lib/util/thermostats.ts new file mode 100644 index 00000000..3ccdd762 --- /dev/null +++ b/src/lib/util/thermostats.ts @@ -0,0 +1,140 @@ +import { ClimateSetting } from "lib/zod/climate_setting.ts" + +export const normalizeClimateSettingMode = (cs: Partial) => { + return { + automatic_heating_enabled: + cs.automatic_heating_enabled ?? + deriveAutomaticHeatingEnabledFromHvacModeSetting(cs.hvac_mode_setting), + automatic_cooling_enabled: + cs.automatic_cooling_enabled ?? + deriveAutomaticCoolingEnabledFromHvacModeSetting(cs.hvac_mode_setting), + hvac_mode_setting: + cs.hvac_mode_setting ?? + deriveHvacModeSettingFromFlags({ + automatic_cooling_enabled: cs.automatic_cooling_enabled!, + automatic_heating_enabled: cs.automatic_heating_enabled!, + }), + } +} + +/** + * @param {ClimateSetting} cs + * @returns {ClimateSetting} + * @description + * This function takes a Partial and returns a new ClimateSetting with all fields filled in + * + */ +export const normalizeClimateSetting = ( + cs: Partial +): ClimateSetting => { + if (cs.manual_override_allowed === undefined) + throw new Error("manual_override_allowed is required") + + let normalized_climate_setting: ClimateSetting = { + ...normalizeClimateSettingMode(cs), + manual_override_allowed: cs.manual_override_allowed, + } + + // if the climate setting calls for cooling set points, we set them + if (normalized_climate_setting.automatic_cooling_enabled) { + normalized_climate_setting = { + ...normalized_climate_setting, + ...normalizeCoolingSetPoints(cs), + } + } + + // likewise for heating + if (normalized_climate_setting.automatic_heating_enabled) { + normalized_climate_setting = { + ...normalized_climate_setting, + ...normalizeHeatingSetPoints(cs), + } + } + + return normalized_climate_setting +} + +export const deriveAutomaticHeatingEnabledFromHvacModeSetting = ( + hvac_mode_setting?: ClimateSetting["hvac_mode_setting"] +) => hvac_mode_setting === "heat" || hvac_mode_setting === "heatcool" + +export const deriveAutomaticCoolingEnabledFromHvacModeSetting = ( + hvac_mode_setting?: ClimateSetting["hvac_mode_setting"] +) => hvac_mode_setting === "cool" || hvac_mode_setting === "heatcool" + +export const deriveHvacModeSettingFromFlags = ({ + automatic_cooling_enabled, + automatic_heating_enabled, +}: { + automatic_cooling_enabled: boolean + automatic_heating_enabled: boolean +}): ClimateSetting["hvac_mode_setting"] => { + if (automatic_cooling_enabled && automatic_heating_enabled) return "heatcool" + if (automatic_cooling_enabled) return "cool" + if (automatic_heating_enabled) return "heat" + return "off" +} + +/** + * + * @param cs + * @returns {Partial} + * This will look at what cooling set point is on the climate setting, and set the other temperature scale if neccessary. + * For example, if a request comes in with a cooling_set_point_ceslius, we will set the cooling_set_point_fahrenheit as well. + * + */ +const normalizeCoolingSetPoints = (cs: Partial) => { + const setPoints: Partial = {} + + if (cs.cooling_set_point_celsius !== undefined) { + setPoints.cooling_set_point_celsius = cs.cooling_set_point_celsius + } else if (cs.cooling_set_point_fahrenheit !== undefined) { + setPoints.cooling_set_point_celsius = convertToCelsius( + cs.cooling_set_point_fahrenheit + ) + } + + if (cs.cooling_set_point_fahrenheit !== undefined) { + setPoints.cooling_set_point_fahrenheit = cs.cooling_set_point_fahrenheit + } else if (cs.cooling_set_point_celsius !== undefined) { + setPoints.cooling_set_point_fahrenheit = convertToFahrenheit( + cs.cooling_set_point_celsius + ) + } + return setPoints +} + +/** + * + * @param cs + * @returns {Partial} + * This will look at what heating set point is on the climate setting, and set the other temperature scale. + * For example, if a request comes in with a heating_set_point_ceslius, we will set the heating_set_point_fahrenheit as well. + * + */ +const normalizeHeatingSetPoints = (cs: Partial) => { + const setPoints: Partial = {} + + if (cs.heating_set_point_celsius !== undefined) { + setPoints.heating_set_point_celsius = cs.heating_set_point_celsius + } else if (cs.heating_set_point_fahrenheit !== undefined) { + setPoints.heating_set_point_celsius = convertToCelsius( + cs.heating_set_point_fahrenheit + ) + } + + if (cs.heating_set_point_fahrenheit !== undefined) { + setPoints.heating_set_point_fahrenheit = cs.heating_set_point_fahrenheit + } else if (cs.heating_set_point_celsius !== undefined) { + setPoints.heating_set_point_fahrenheit = convertToFahrenheit( + cs.heating_set_point_celsius + ) + } + + return setPoints +} + +export const convertToFahrenheit = (celsius: number) => (celsius * 9) / 5 + 32 + +export const convertToCelsius = (fahrenheit: number) => + (fahrenheit - 32) * (5 / 9) diff --git a/src/pages/api/thermostats/climate_setting_schedules/create.ts b/src/pages/api/thermostats/climate_setting_schedules/create.ts index 933b14f5..db3d14f1 100644 --- a/src/pages/api/thermostats/climate_setting_schedules/create.ts +++ b/src/pages/api/thermostats/climate_setting_schedules/create.ts @@ -5,6 +5,7 @@ import { withRouteSpec } from "lib/middleware/with-route-spec.ts" import { climate_setting } from "lib/zod/climate_setting.ts" import { climate_setting_schedule } from "lib/zod/climate_setting_schedule.ts" import { timestamp } from "lib/zod/common.ts" +import { normalizeClimateSetting } from "lib/util/thermostats.ts" const jsonBody = z .object({ @@ -53,7 +54,7 @@ export default withRouteSpec({ name: name ?? `Schedule ${randomUUID().slice(5)}`, schedule_starts_at: new Date(schedule_starts_at).toISOString(), schedule_ends_at: new Date(schedule_ends_at).toISOString(), - ...climate_setting_for_schedule, + ...normalizeClimateSetting(climate_setting_for_schedule), }) res.status(200).json({