Solid state relay is probably a bad idea with all the extra heat-sinking, extra cost, and chance of getting counterfeits.

I do this with ESPHome & a J115F21C12VDCS.9 relay (note only the NO side is rated for 40A resistive): https://i.imgur.com/MqqOkoY.png

Choose any of the temperature sensors here for air temperature sensing: https://esphome.io/

Configuration is so easy. For the sensor, just copy the config from here, for example: https://esphome.io/components/sensor/bme280. Add a gpio output (https://esphome.io/components/output/gpio) and a bang-bang climate controller (https://esphome.io/components/climate/bang_bang.html)

Here's the kicad footprint for that relay (Relay_SPDT_CIT-J115F2.kicad_mod) if you need it:

(module Relay_SPDT_CIT-J115F2 (layer F.Cu) (tedit 611825E5)
(descr https://www.citrelay.com/Catalog%20Pages/RelayCatalog/J115F2.pdf)
(tags "Relay CIT J115F2 SPDT")
(fp_text reference REF** (at 12.1666 -15.2) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value Relay_SPDT_CIT-J115F2 (at 18.542 13.9 180) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start -3.2 -14.1) (end 27.08 -14.1) (layer F.Fab) (width 0.1))
(fp_line (start -3.96 -13.34) (end -3.96 11.94) (layer F.Fab) (width 0.1))
(fp_line (start 27.84 -13.34) (end 27.84 11.94) (layer F.Fab) (width 0.1))
(fp_line (start -3.2 12.7) (end 27.08 12.7) (layer F.Fab) (width 0.1))
(fp_line (start -4.3 -14.3) (end 28.09 -14.3) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.3 -14.3) (end -4.3 12.95) (layer F.CrtYd) (width 0.05))
(fp_line (start 28.09 -14.3) (end 28.09 12.95) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.3 12.95) (end 28.09 12.95) (layer F.CrtYd) (width 0.05))
(fp_line (start 17.74 1.778) (end 19.304 1.778) (layer F.SilkS) (width 0.12))
(fp_line (start 23.876 1.778) (end 25.34 1.778) (layer F.SilkS) (width 0.12))
(fp_line (start 25.34 1.778) (end 25.34 6.604) (layer F.SilkS) (width 0.12))
(fp_line (start 21.59 -8.9) (end 17.526 -8.9) (layer F.SilkS) (width 0.12))
(fp_line (start 21.59 -2.54) (end 21.59 -8.9) (layer F.SilkS) (width 0.12))
(fp_line (start 21.59 -2.54) (end 22.8092 2.032) (layer F.SilkS) (width 0.12))
(fp_line (start 17.74 6.604) (end 17.74 1.778) (layer F.SilkS) (width 0.12))
(fp_line (start 22.86 1.778) (end 23.876 1.016) (layer F.SilkS) (width 0.12))
(fp_line (start 23.876 2.54) (end 22.86 1.778) (layer F.SilkS) (width 0.12))
(fp_line (start 20.32 1.778) (end 19.304 1.016) (layer F.SilkS) (width 0.12))
(fp_line (start 19.304 2.54) (end 20.32 1.778) (layer F.SilkS) (width 0.12))
(fp_line (start 23.876 2.54) (end 23.876 1.016) (layer F.SilkS) (width 0.12))
(fp_line (start 19.304 1.016) (end 19.304 2.54) (layer F.SilkS) (width 0.12))
(fp_circle (center 21.59 -2.54) (end 21.59 -2.413) (layer F.SilkS) (width 0.12))
(fp_line (start 4.064 -5.1054) (end 5.334 -5.1054) (layer F.SilkS) (width 0.12))
(fp_line (start 5.334 5.1054) (end 4.064 5.1054) (layer F.SilkS) (width 0.12))
(fp_line (start 5.334002 5.105397) (end 5.334002 1.777997) (layer F.SilkS) (width 0.12))
(fp_line (start 5.334 -1.778) (end 5.334 -5.1054) (layer F.SilkS) (width 0.12))
(fp_line (start -3.2 12.81) (end 27.08 12.81) (layer F.SilkS) (width 0.12))
(fp_line (start 27.95 -13.34) (end 27.95 11.94) (layer F.SilkS) (width 0.12))
(fp_line (start -4.07 -13.34) (end -4.07 11.94) (layer F.SilkS) (width 0.12))
(fp_line (start -3.2 -14.21) (end 27.08 -14.21) (layer F.SilkS) (width 0.12))
(fp_arc (start 27.08 -13.34) (end 27.84 -13.34) (angle -90) (layer F.Fab) (width 0.1))
(fp_arc (start 27.08 11.94) (end 27.08 12.7) (angle -90) (layer F.Fab) (width 0.1))
(fp_arc (start -3.2 11.94) (end -3.96 11.94) (angle -90) (layer F.Fab) (width 0.1))
(fp_arc (start -3.2 -13.34) (end -3.2 -14.1) (angle -90) (layer F.Fab) (width 0.1))
(fp_arc (start 5.334001 -0.381001) (end 5.842 -0.762001) (angle -286.3) (layer F.SilkS) (width 0.12))
(fp_arc (start 5.334002 0.380998) (end 5.842001 -0.000002) (angle -286.3) (layer F.SilkS) (width 0.12))
(fp_arc (start 5.334 -1.143) (end 5.334 -1.778) (angle -233.1) (layer F.SilkS) (width 0.12))
(fp_arc (start 5.334002 1.142997) (end 5.334002 1.777997) (angle 233.1) (layer F.SilkS) (width 0.12))
(fp_text user %R (at 12.3952 0) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_arc (start -3.2 -13.34) (end -3.2 -14.21) (angle -90) (layer F.SilkS) (width 0.12))
(fp_arc (start -3.2 11.94) (end -4.07 11.94) (angle -90) (layer F.SilkS) (width 0.12))
(fp_arc (start 27.08 -13.34) (end 27.95 -13.34) (angle -90) (layer F.SilkS) (width 0.12))
(fp_arc (start 27.08 11.94) (end 27.08 12.81) (angle -90) (layer F.SilkS) (width 0.12))
(pad A2 thru_hole circle (at 2.54 5.1 180) (size 2 2) (drill 1.1) (layers *.Cu *.Mask))
(pad 14 thru_hole circle (at 17.74 8.9 180) (size 4 4) (drill 2.1) (layers *.Cu *.Mask))
(pad 12 thru_hole circle (at 25.34 8.9 180) (size 4 4) (drill 2.1) (layers *.Cu *.Mask))
(pad 11 thru_hole circle (at 15.2 -8.9 180) (size 4 4) (drill 2.1) (layers *.Cu *.Mask))
(pad A1 thru_hole roundrect (at 2.54 -5.1 180) (size 2 2) (drill 1.1) (layers *.Cu *.Mask) (roundrect_rratio 0.25))
(model ${KISYS3DMOD}/Relay_THT.3dshapes/Relay_SPDT_RAYEX-L90S.wrl
(at (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)