   digitalWrite(fanRelay, HIGH);
   digitalWrite(bypassRelay, HIGH);
 
+  while (Blynk.connect() == false) {
+    // Wait until connected
+  }
+
   // START OTA ROUTINE
-  ArduinoOTA.setHostname("hvacMonitor");     // Name that is displayed in the Arduino IDE.
+  ArduinoOTA.setHostname("hvacMonitor");     // Port name that is displayed in the Arduino IDE.
 
   ArduinoOTA.onStart([]() {
     Serial.println("Start");

  timer.run();
  ArduinoOTA.handle();

  if (ranOnce == false && year() != 1970) {
    Blynk.setProperty(V107, "label", String(hour()) + ":" + minute() + " " + month() + "/" + day() + " HVAC");
    ranOnce = true;
  }
}

void disconnectWatcher() {
  if (Blynk.connected() == false && blynkFlag == false) {
    ++blynkDisconnectCount;
    blynkFlag = true;
  }
  else if (Blynk.connected() == true && blynkFlag == true) {
    blynkFlag = false;
  }
}

void disconnectReporter() {
  Blynk.virtualWrite(V107, blynkDisconnectCount);
}


// ******************************************************** HVAC CTRL START **********************************************
BLYNK_WRITE(V40) {
  switch (param.asInt())
  {
    // **************************************** IN ALL CASES BELOW: LOW == ON/CLOSED, HIGH == OFF/OPEN
    case 1: {                         // House t-stat controlling (bypass off)
        controlMode = 0;
        controllingSpace = "";
        Blynk.setProperty(V39, "label", "Setpoint");
        digitalWrite(fanRelay, HIGH);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, HIGH);
        break;
      }
    case 2: {                         // House t-stat bypass (Blynk-controlling, but nothing running)
        controlMode = 0;
        controllingSpace = "";
        Blynk.setProperty(V39, "label", "Setpoint");
        digitalWrite(fanRelay, HIGH);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, LOW);
        break;
      }
    case 3: {                         // Manual fan (blower only)
        controlMode = 0;
        controllingSpace = "";
        Blynk.setProperty(V39, "label", "Setpoint");
        digitalWrite(fanRelay, LOW);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, LOW);
        break;
      }
    case 4: {                         // House t-stat-controlled cooling
        controlMode = 1;
        controllingSpace = "AT";
        coolingMode = 1;
        controllingTemp = tempAT;
        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by AT (currently " + tempAT + "F).");
        digitalWrite(fanRelay, HIGH);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, LOW);
        break;
      }
    case 5: {                         // House t-stat controlled heating
        /*
          controlMode = 1;
          controllingSpace = "AT";
          coolingMode = 0;
          controllingTemp = tempAT;
          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by AT (currently " + tempAT + "F).");
          digitalWrite(fanRelay, HIGH);
          digitalWrite(coolingRelay, HIGH);
          digitalWrite(heatingRelay, HIGH);
          digitalWrite(bypassRelay, LOW);
          break;
        */
      }
    case 6: {                                         // Keaton-controlled cooling
        controlMode = 1;
        controllingSpace = "KK";                      // Here's the room that we're watching
        coolingMode = 1;                              // This sets the HVAC in correct mode and decides tstat logic
        controllingTemp = tempKK;
        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by KK (currently " + tempKK + "F).");
        digitalWrite(fanRelay, HIGH);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, LOW);
        break;
      }
    case 7: {                             // Keaton-controlled heating
        /*
          Blynk.syncVirtual(V39);
          controlMode = 1;
          controllingSpace = "KK";
          coolingMode = 0;
          controllingTemp = tempKK;
          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by KK (currently " + tempKK + "F).");
          digitalWrite(fanRelay, HIGH);
          digitalWrite(coolingRelay, HIGH);
          digitalWrite(heatingRelay, HIGH);
          digitalWrite(bypassRelay, LOW);
          break;
        */
      }
    case 8: {                             // Liv-controlled cooling
        controlMode = 1;
        controllingSpace = "LK";
        coolingMode = 1;
        controllingTemp = tempLK;
        Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by LK (currently " + tempLK + "F).");
        digitalWrite(fanRelay, HIGH);
        digitalWrite(coolingRelay, HIGH);
        digitalWrite(heatingRelay, HIGH);
        digitalWrite(bypassRelay, LOW);
        break;
      }
    case 9: {                             // Liv-controlled heating
        /*
          controlMode = 1;
          controllingSpace = "LK";
          coolingMode = 0;
          controllingTemp = tempLK;
          Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + ". Controlled by LK (currently " + tempLK + "F).");
          digitalWrite(fanRelay, HIGH);
          digitalWrite(coolingRelay, HIGH);
          digitalWrite(heatingRelay, HIGH);
          digitalWrite(bypassRelay, LOW);
          break;
        */
      }
    default: {
        break;
      }
  }
}

void tempControl() {

  if (controlMode == 1 && setpointTemp != 0 && controllingTemp != 0)     // If we're in bypass mode, and established setpoint and control temps, then proceed:
  {

    if (coolingMode == 0 && controllingTemp < setpointTemp)       // If mode is heat and room falls equal or below the target temp...
    {
      digitalWrite(heatingRelay, LOW);                            // turn on the heat. But when
      controlRuntime = controlRuntime + 30;
    }
    else if (coolingMode == 0 && controllingTemp >= (setpointTemp + controlSplit))   // If mode is heat and room is warmer then target temp...
    {
      digitalWrite(heatingRelay, HIGH);                           // everything is turned off.
      controlRuntime = 0;
      controlLatch = FALSE;
    }
    else if (coolingMode == 1 && controllingTemp > setpointTemp)  // If mode is cool and room rises equal or above the target temp...
    {
      digitalWrite(coolingRelay, LOW);                            // turn on cooling. Then,
      controlRuntime = controlRuntime + 30;
    }
    else if (coolingMode == 1 && controllingTemp <= (setpointTemp - controlSplit))   // when mode is cool and room is cooler then target temp...
    {
      digitalWrite(coolingRelay, HIGH);                           // turn everything off.
      controlRuntime = 0;
      controlLatch = FALSE;
    }

  }
}

void tempControlSafety() {      // Safeties running every second
  if (controlMode == 1 && digitalRead(blowerPin) == LOW && (setpointTemp <= 0 || controllingTemp  <= 0) )  // If HVAC is running and the setpoint or controlling temp are 0, then open relays. Control will be locked out until those temps are != 0.
  {
    safetyShutoff();

    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Ctrl attempted with no setpointTemp or controllingTemp");
  }

  if (controlMode == 1 && digitalRead(blowerPin) == LOW && controlLatch == FALSE) {  // If Blynk is controlling, and fan starts:
    controlStart = millis();            // Records start time of unit.
    controlLatch = TRUE;
  }

  if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 0 && millis() >= controlStart + heatControlTimeout) {
    safetyShutoff();

    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Heater ran for more than " + (heatControlTimeout / 60000) + " minutes.");
  }
  else if (controlMode == 1 && digitalRead(blowerPin) == LOW && coolingMode == 1 && millis() >= controlStart + coolControlTimeout) {
    safetyShutoff();

    Blynk.notify(hour() + String(":") + minute() + ":" + second() + " " + month() + "/" + day() + " ERROR: Cooler ran for more than " + (coolControlTimeout / 60000) + " minutes.");
  }

}

void safetyShutoff() {
  controlMode = 0;
  controllingSpace = "";
  controlLatch = FALSE;
  Blynk.setProperty(V39, "label", "Setpoint");
  digitalWrite(fanRelay, HIGH);
  digitalWrite(coolingRelay, HIGH);
  digitalWrite(heatingRelay, HIGH);
  digitalWrite(bypassRelay, HIGH);
}

void tempUpdater() {
  Blynk.syncVirtual(V3);  // AT
  Blynk.syncVirtual(V4);  // KK
  Blynk.syncVirtual(V6);  // LK

  if (controllingSpace == "KK")
  {
    controllingTemp = tempKK;
    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by KK (currently " + tempKK + "F).");
  }
  else if (controllingSpace == "LK")
  {
    controllingTemp = tempLK;
    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by LK (currently " + tempLK + "F).");
  }
  else if (controllingSpace == "AT")
  {
    controllingTemp = tempAT;
    Blynk.setProperty(V39, "label", String("Setpoint: ") + setpointTemp + "F. Controlled by AT (currently " + tempAT + "F).");
  }
}

BLYNK_WRITE(V3) {
  tempAT = param.asInt();
}

BLYNK_WRITE(V4) {
  tempKK = param.asInt();
}

BLYNK_WRITE(V6) {
  tempLK = param.asInt();
}

BLYNK_WRITE(V39)  // Input from slider in app to set setpoint
{
  setpointTemp = param.asInt();
}

void controlReset() {
  controlMode = 0;
  controllingSpace = "";
  Blynk.setProperty(V39, "label", "Setpoint");
  digitalWrite(fanRelay, HIGH);
  digitalWrite(coolingRelay, HIGH);
  digitalWrite(heatingRelay, HIGH);
  digitalWrite(bypassRelay, HIGH);
  Blynk.virtualWrite(V40, 1);

}
// ******************************************************** HVAC CTRL END **********************************************

void vsync1()
{
  Blynk.syncVirtual(V16);    // currentStatus
  Blynk.syncVirtual(V39);    // setpointTemp
}

void vsync2()
{
  Blynk.syncVirtual(V110);    // hvacTodaysStartCount
  Blynk.syncVirtual(V111);    // todaysAccumRuntimeSec
}

void vsync3()
{
  Blynk.syncVirtual(V112);    // yesterdayRuntime
  Blynk.syncVirtual(V11);     // currentFilterSec
}

BLYNK_WRITE(V110)
{
  hvacTodaysStartCount = param.asInt();
}

BLYNK_WRITE(V111)
{
  todaysAccumRuntimeSec = param.asLong();
}

BLYNK_WRITE(V112)
{
  yesterdayRuntime = param.asInt();
}

BLYNK_WRITE(V11)
{
  currentFilterSec = param.asLong();
}

BLYNK_WRITE(V16)
{
  currentStatus = param.asStr();
}

void phantSend() {
  if (digitalRead(blowerPin) == LOW)        // If HVAC blower motor is running...
  {
    runStatus = 40;
  }
  else if (digitalRead(blowerPin) == HIGH)  // If HVAC blower motor if off...
  {
    runStatus = NULL;
  }

  // The following changes the color of Time Until Filter Change in app... placed here because the timing.
  if (filterChangeHours - (currentFilterSec / 3600) > 24 )
  {
    Blynk.setProperty(V10, "color", "#23C48E");   // Green
  }
  else if (filterChangeHours - (currentFilterSec / 3600) < 24 &&  filterChangeHours - (currentFilterSec / 3600) > 8)
  {
    Blynk.setProperty(V10, "color", "#ED9D00");   // Yellow
  }
  else if (filterChangeHours - (currentFilterSec / 3600) < 8)
  {
    Blynk.setProperty(V10, "color", "#D3435C");   // Red
  }

  //Blynk.virtualWrite(67, String("returntemp=") + tempRA + "&runstatus=" + runStatus + "&supplytemp=" + tempSA);

  Serial.print("connecting to ");
  Serial.println(hostSF);

  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 8080;
  if (!client.connect(hostSF, httpPort)) {
    Serial.println("connection failed");
    return;
  }

  Serial.print("Requesting...");

  // This will send the request to the server
  client.print(String("GET ") + "/input/" + streamId + "?private_key=" + privateKey + "&returntemp=" + tempRA + "&runstatus=" + runStatus + "&supplytemp=" + tempSA + " HTTP/1.1\r\n" +
               "Host: " + hostSF + "\r\n" +
               "Connection: close\r\n\r\n");
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > 15000) {
      Serial.println(">>> Client Timeout !");
      client.stop();
      return;
    }
  }

  // Read all the lines of the reply from server and print them to Serial
  while (client.available()) {
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }

  Serial.println();
  Serial.println("closing connection");
}

BLYNK_WRITE(V19)
{
  int pinData = param.asInt();

  if (pinData == 0)
  {
    Blynk.setProperty(V16, "label", "Current Status:                    Hi/Lo temps above are last 24h");
  }

  if (pinData == 1)
  {
    Blynk.setProperty(V16, "label", "Current Status:                    Hi/Lo temps above are since midnight");
  }
}

BLYNK_WRITE(V18)              // App button to display all terminal commands available
{
  int pinData = param.asInt();

  if (pinData == 0) // Triggers when button is released only
  {
    terminal.println(" "); terminal.println(" "); terminal.println(" "); terminal.println(" ");
    terminal.println(" "); terminal.println(" "); terminal.println(" "); terminal.println(" ");
    terminal.println("  --------- AVAILABLE COMMANDS ----------");
    terminal.println(" ");
    terminal.println(" filter : HVAC filter menu");
    terminal.println(" WU : Change WU API station");
    terminal.println(" ");
    terminal.flush();
  }
}

BLYNK_WRITE(V27) // App button to report uptime
{
  int pinData = param.asInt();



    if (minDur < 121)
    {
      terminal.print(String("HVAC: ") + minDur + " min/");
      terminal.print(WiFi.RSSI());
      terminal.print("/");
      terminal.println(WiFi.localIP());
    }
    else if (minDur > 120)
    {
      terminal.print(String("HVAC: ") + hourDur + " hr/");
      terminal.print(WiFi.RSSI());
      terminal.print("/");
      terminal.println(WiFi.localIP());
    }
    terminal.flush();
  }
}
