From abbbd16b5c4f15ccb3096358f7a0b43d44d37c46 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Sat, 2 May 2026 21:34:29 +0200 Subject: [PATCH] Version with no VFD built --- .gitignore | 1 + .../Gaugecontroller_no_VFD.ino | 1102 ++++++++ Gaugecontroller_no_VFD/esp-home_no_VFD.yaml | 2395 +++++++++++++++++ 3 files changed, 3498 insertions(+) create mode 100644 Gaugecontroller_no_VFD/Gaugecontroller_no_VFD.ino create mode 100644 Gaugecontroller_no_VFD/esp-home_no_VFD.yaml diff --git a/.gitignore b/.gitignore index e257658..7f3d77e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ *.out *.app +.codex diff --git a/Gaugecontroller_no_VFD/Gaugecontroller_no_VFD.ino b/Gaugecontroller_no_VFD/Gaugecontroller_no_VFD.ino new file mode 100644 index 0000000..82f247a --- /dev/null +++ b/Gaugecontroller_no_VFD/Gaugecontroller_no_VFD.ino @@ -0,0 +1,1102 @@ +#include +#include +#include +#include + +static const uint8_t GAUGE_COUNT = 4; + +// Backlight/status LEDs and indicator LEDs use separate data strips because +// their LED chipsets are not compatible on one chain. The command protocol +// still exposes one logical LED segment per gauge. +static const uint8_t LED_DATA_PIN = 22; +static const uint8_t INDICATOR_LED_DATA_PIN = 36; +static const uint8_t BREATHE_FRAME_MS = 16; +static const uint8_t LED_SHOW_MIN_INTERVAL_MS = 16; +static const uint8_t RX_LINE_MAX = 120; + +// For now, command and debug traffic share the same serial port. +#define CMD_PORT Serial1 +#define DEBUG_PORT Serial1 +static const unsigned long SERIAL_BAUD = 38400; + +struct GaugePins { + uint8_t dirPin; + uint8_t stepPin; + int8_t enablePin; // -1 means there is no enable pin + bool dirInverted; + bool stepActiveHigh; + bool enableActiveLow; + const char* ledOrder; // one char per LED: 'G' = GRB, 'R' = RGB; length defines ledCount +}; + +constexpr GaugePins gaugePins[GAUGE_COUNT] = { + // dir, step, en, dirInv, stepHigh, enActiveLow, ledOrder + {48, 49, -1, false, true, true, "RRRGGRR"}, // Gauge 0 + {8, 9, -1, true, true, true, "GGGRRRR"}, // Gauge 1 + {52, 53, -1, false, true, true, "GGGRRRR"}, // Gauge 2 + {50, 51, -1, false, true, true, "GGGRRRR"}, // Gauge 3 +}; + +constexpr uint8_t cstrLen(const char* s) { + return *s ? uint8_t(1 + cstrLen(s + 1)) : uint8_t(0); +} + +constexpr uint8_t sumLedCounts(uint8_t i = 0) { + return i >= GAUGE_COUNT ? 0 : cstrLen(gaugePins[i].ledOrder) + sumLedCounts(i + 1); +} +static const uint8_t TOTAL_LEDS = sumLedCounts(); + +constexpr bool isIndicatorLedIndex(uint8_t localIdx) { + return localIdx == 3 || localIdx == 4; +} + +constexpr uint8_t countIndicatorLedsForGauge(uint8_t gaugeIdx) { + return (cstrLen(gaugePins[gaugeIdx].ledOrder) > 3 ? 1 : 0) + + (cstrLen(gaugePins[gaugeIdx].ledOrder) > 4 ? 1 : 0); +} + +constexpr uint8_t sumIndicatorLedCounts(uint8_t i = 0) { + return i >= GAUGE_COUNT ? 0 : countIndicatorLedsForGauge(i) + sumIndicatorLedCounts(i + 1); +} +static const uint8_t TOTAL_INDICATOR_LEDS = sumIndicatorLedCounts(); +static const uint8_t TOTAL_MAIN_LEDS = TOTAL_LEDS - TOTAL_INDICATOR_LEDS; + +enum HomingState : uint8_t { + HS_IDLE, + HS_START, + HS_BACKING, + HS_SETTLE, + HS_DONE +}; + +struct Gauge { + long currentPos = 0; + long targetPos = 0; + + long minPos = 0; + long maxPos = 3780; + long homingBackoffSteps = 3800; // Deliberately a touch past full reverse travel. + + float velocity = 0.0f; + uint32_t maxSpeed = 4000; + uint32_t accel = 6000; + uint32_t homingSpeed = 500; + uint32_t homingStepIntervalUs = 2000; + + float stepAccumulator = 0.0f; + unsigned long lastUpdateMicros = 0; + + bool enabled = true; + bool homed = false; + bool dirKnown = false; + bool dirForward = true; + + HomingState homingState = HS_IDLE; + long homingStepsRemaining = 0; + unsigned long homingLastStepMicros = 0; + unsigned long homingStateStartMs = 0; + + bool sweepEnabled = false; + bool sweepTowardMax = true; +}; + +enum LedFx : uint8_t { FX_BLINK = 0, FX_BREATHE = 1, FX_DFLASH = 2 }; + +struct BlinkState { + bool active = false; + LedFx fx = FX_BLINK; + CRGB onColor; + unsigned long lastMs = 0; + uint16_t onMs = 500; + uint16_t offMs = 500; + bool currentlyOn = false; + uint16_t periodMs = 2000; + uint16_t cyclePos = 0; + uint8_t dphase = 0; +}; + +Gauge gauges[GAUGE_COUNT]; +char rxLine[RX_LINE_MAX + 1]; +uint8_t rxLen = 0; +bool rxOverflowed = false; + +CRGB logicalLeds[TOTAL_LEDS]; +CRGB mainLeds[TOTAL_MAIN_LEDS]; +CRGB indicatorLeds[TOTAL_INDICATOR_LEDS]; +CLEDController* mainLedController = nullptr; +CLEDController* indicatorLedController = nullptr; +uint8_t gaugeLedOffset[GAUGE_COUNT]; +uint8_t gaugeLedCount[GAUGE_COUNT]; +uint8_t gaugeMainLedOffset[GAUGE_COUNT]; +uint8_t gaugeIndicatorLedOffset[GAUGE_COUNT]; +uint8_t ledPhysicalIdx[TOTAL_LEDS]; +bool ledIsIndicator[TOTAL_LEDS]; +bool ledRgSwap[TOTAL_LEDS]; +BlinkState blinkState[TOTAL_LEDS]; +bool mainLedsDirty = false; +bool indicatorLedsDirty = false; +unsigned long lastLedShowMs = 0; + +// FastLED drives the shared strip as RGB. Each gauge's ledOrder string marks per-LED +// type ('R' = RGB, 'G' = GRB); writes to GRB-ordered LEDs pre-swap R and G to compensate. +inline bool ledNeedsRgSwap(uint8_t globalIdx) { + return ledRgSwap[globalIdx]; +} + +inline CRGB encodeForStrip(uint8_t globalIdx, CRGB color) { + if (ledNeedsRgSwap(globalIdx)) { + uint8_t tmp = color.r; + color.r = color.g; + color.g = tmp; + } + return color; +} + +inline void writeLed(uint8_t globalIdx, CRGB color) { + logicalLeds[globalIdx] = color; + + if (ledIsIndicator[globalIdx]) { + indicatorLeds[ledPhysicalIdx[globalIdx]] = encodeForStrip(globalIdx, color); + indicatorLedsDirty = true; + } else { + mainLeds[ledPhysicalIdx[globalIdx]] = encodeForStrip(globalIdx, color); + mainLedsDirty = true; + } +} + +inline CRGB readLed(uint8_t globalIdx) { + return logicalLeds[globalIdx]; +} + +void showDirtyLeds() { + if (!mainLedsDirty && !indicatorLedsDirty) return; + + unsigned long nowMs = millis(); + if (nowMs - lastLedShowMs < LED_SHOW_MIN_INTERVAL_MS) return; + + if (mainLedsDirty && mainLedController != nullptr) { + mainLedController->showLeds(255); + mainLedsDirty = false; + } + if (indicatorLedsDirty && indicatorLedController != nullptr) { + indicatorLedController->showLeds(255); + indicatorLedsDirty = false; + } + lastLedShowMs = nowMs; +} + +// Sends one-line command replies back over the control port. +// +// Serial protocol summary. +// +// Host -> controller commands (newline-terminated ASCII): +// SET +// SPEED +// ACCEL +// ENABLE <0|1> +// ZERO +// HOME +// HOMEALL +// SWEEP +// POS? +// LED? +// LED +// BLINK [ ] +// BREATHE +// DFLASH +// PING +// +// Controller -> host replies / events: +// READY +// Sent once from setup() after boot completes. +// OK +// Sent after a valid mutating command, and after POS?/LED? once all data lines +// for that query have been emitted. +// PONG +// Sent in response to PING. +// ERR BAD_CMD +// Sent when a complete line matches no parser. +// ERR TOO_LONG +// Sent when an input line exceeds the receive buffer limit. +// ERR BAD_ID +// Sent by commands that take a gauge id when the id is outside 0..GAUGE_COUNT-1. +// ERR BAD_SPEED +// Sent by SPEED when the requested speed is <= 0. +// ERR BAD_ACCEL +// Sent by ACCEL when the requested acceleration is <= 0. +// ERR BAD_IDX +// Sent by LED/BLINK/BREATHE/DFLASH when an LED index or range is invalid. +// ERR BAD_TIME +// Sent by BLINK/BREATHE when the timing parameter is invalid. +// POS +// Emitted once per gauge before the trailing OK reply to POS?. +// LED +// Emitted once per configured LED before the trailing OK reply to LED?. +// HOMED +// Debug event printed on DEBUG_PORT when a homing sequence settles successfully. +void sendReply(const char* s) { + CMD_PORT.println(s); +} + +void sendReply(const __FlashStringHelper* s) { + CMD_PORT.println(s); +} + +char* trimLine(char* s) { + while (*s == ' ' || *s == '\t') s++; + char* end = s + strlen(s); + while (end > s && (end[-1] == ' ' || end[-1] == '\t')) { + *--end = '\0'; + } + return s; +} + +inline void skipSpaces(const char*& s) { + while (*s == ' ' || *s == '\t') s++; +} + +bool commandArgs(const char* line, const char* command, const char*& args) { + size_t len = strlen(command); + if (strncmp(line, command, len) != 0) return false; + if (line[len] != ' ' && line[len] != '\t') return false; + + args = line + len; + skipSpaces(args); + return true; +} + +bool parseIntegerToken(const char*& s, long& value) { + bool negative = false; + bool sawDigit = false; + long parsed = 0; + + if (*s == '+' || *s == '-') { + negative = (*s == '-'); + s++; + } + + while (*s >= '0' && *s <= '9') { + sawDigit = true; + parsed = parsed * 10 + (*s - '0'); + s++; + } + + if (*s == '.' || *s == ',') { + s++; + while (*s >= '0' && *s <= '9') { + s++; + } + } + + if (!sawDigit) return false; + + value = negative ? -parsed : parsed; + skipSpaces(s); + return true; +} + +// Tiny float absolute-value helper to avoid dragging more machinery into the sketch. +float absf(float x) { + return (x < 0.0f) ? -x : x; +} + +// Updates the cached enable state and toggles the hardware pin if one exists. +void setEnable(uint8_t id, bool en) { + if (id >= GAUGE_COUNT) return; + gauges[id].enabled = en; + + int8_t pin = gaugePins[id].enablePin; + if (pin < 0) return; + + bool level = gaugePins[id].enableActiveLow ? !en : en; + digitalWrite(pin, level ? HIGH : LOW); +} + +// Applies the logical direction after accounting for per-gauge inversion. +void setDir(uint8_t id, bool forward) { + Gauge& g = gauges[id]; + if (g.dirKnown && g.dirForward == forward) return; + + bool level = gaugePins[id].dirInverted ? !forward : forward; + digitalWrite(gaugePins[id].dirPin, level ? HIGH : LOW); + g.dirKnown = true; + g.dirForward = forward; +} + +// Emits one step pulse with the polarity expected by the driver. +void pulseStep(uint8_t id) { + bool active = gaugePins[id].stepActiveHigh; + digitalWrite(gaugePins[id].stepPin, active ? HIGH : LOW); + delayMicroseconds(4); + digitalWrite(gaugePins[id].stepPin, active ? LOW : HIGH); +} + +// Moves the motor by one step if the requested direction is still within allowed travel. +void doStep(uint8_t id, int dir, bool allowPastMin = false) { + Gauge& g = gauges[id]; + if (!g.enabled) return; + + if (dir > 0) { + if (g.currentPos >= g.maxPos) return; + setDir(id, true); + pulseStep(id); + g.currentPos++; + } else if (dir < 0) { + if (!allowPastMin && g.currentPos <= g.minPos) return; + setDir(id, false); + pulseStep(id); + g.currentPos--; + } +} + +// Arms the homing state machine for one gauge and clears any in-flight motion. +void requestHome(uint8_t id) { + if (id >= GAUGE_COUNT) return; + Gauge& g = gauges[id]; + g.homingState = HS_START; + g.homed = false; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + g.sweepEnabled = false; +} + +// Starts the same homing sequence on every configured gauge. +void requestHomeAll() { + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + requestHome(i); + } +} + +// Advances the simple homing state machine until the gauge is parked at logical zero. +void updateHoming(uint8_t id, unsigned long nowUs, unsigned long nowMs) { + Gauge& g = gauges[id]; + + switch (g.homingState) { + case HS_IDLE: + return; + + case HS_START: + // No endstop here; homing just walks back far enough to hit the hard stop. + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + g.homingStepsRemaining = g.homingBackoffSteps; + g.homingStepIntervalUs = (1000000UL + (g.homingSpeed / 2)) / g.homingSpeed; + if (g.homingStepIntervalUs == 0) g.homingStepIntervalUs = 1; + g.homingLastStepMicros = nowUs; + g.homingState = HS_BACKING; + break; + + case HS_BACKING: { + if ((uint32_t)(nowUs - g.homingLastStepMicros) >= g.homingStepIntervalUs) { + g.homingLastStepMicros += g.homingStepIntervalUs; + + if (g.homingStepsRemaining > 0) { + doStep(id, -1, true); + g.homingStepsRemaining--; + } else { + g.homingState = HS_SETTLE; + g.homingStateStartMs = nowMs; + } + } + break; + } + + case HS_SETTLE: + if (nowMs - g.homingStateStartMs >= 100) { + g.currentPos = 0; + g.targetPos = 0; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + g.homed = true; + g.homingState = HS_DONE; + + DEBUG_PORT.print(F("HOMED ")); + DEBUG_PORT.println(id); + } + break; + + case HS_DONE: + g.homingState = HS_IDLE; + break; + } +} + +// Flips the sweep destination when the gauge has settled at either end of travel. +void updateSweepTarget(uint8_t id) { + Gauge& g = gauges[id]; + if (!g.sweepEnabled || !g.homed || g.homingState != HS_IDLE) return; + + if (g.sweepTowardMax) { + g.targetPos = g.maxPos; + if (g.currentPos >= g.maxPos && absf(g.velocity) < 1.0f) { + g.sweepTowardMax = false; + g.targetPos = g.minPos; + } + } else { + g.targetPos = g.minPos; + if (g.currentPos <= g.minPos && absf(g.velocity) < 1.0f) { + g.sweepTowardMax = true; + g.targetPos = g.maxPos; + } + } +} + +// Runs one gauge worth of motion control, including homing and optional sweeping. +void updateGauge(uint8_t id, unsigned long nowUs, unsigned long nowMs) { + Gauge& g = gauges[id]; + + if (g.homingState != HS_IDLE) { + updateHoming(id, nowUs, nowMs); + return; + } + + if (!g.homed) return; + + if (g.sweepEnabled) { + updateSweepTarget(id); + } + + if (g.lastUpdateMicros == 0) { + g.lastUpdateMicros = nowUs; + return; + } + + float dt = (nowUs - g.lastUpdateMicros) / 1000000.0f; + g.lastUpdateMicros = nowUs; + + if (dt <= 0.0f || dt > 0.1f) return; + + long error = g.targetPos - g.currentPos; + + if (error == 0 && absf(g.velocity) < 0.01f) { + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + return; + } + + float dir = (error > 0) ? 1.0f : (error < 0 ? -1.0f : 0.0f); + // Basic trapezoidal profile: brake if the remaining travel is shorter than the stop distance. + float accel = (float)g.accel; + float maxSpeed = (float)g.maxSpeed; + float brakingDistance = (g.velocity * g.velocity) / (2.0f * accel + 0.0001f); + + if ((float)labs(error) <= brakingDistance) { + if (g.velocity > 0.0f) { + g.velocity -= accel * dt; + if (g.velocity < 0.0f) g.velocity = 0.0f; + } else if (g.velocity < 0.0f) { + g.velocity += accel * dt; + if (g.velocity > 0.0f) g.velocity = 0.0f; + } + } else { + g.velocity += dir * accel * dt; + if (g.velocity > maxSpeed) g.velocity = maxSpeed; + if (g.velocity < -maxSpeed) g.velocity = -maxSpeed; + } + + if (absf(g.velocity) < 0.01f && error != 0) { + g.velocity = dir * 5.0f; + } + + // Integrate fractional steps until there is enough to emit a real pulse. + g.stepAccumulator += g.velocity * dt; + + while (g.stepAccumulator >= 1.0f) { + if (g.currentPos == g.targetPos) { + g.stepAccumulator = 0.0f; + g.velocity = 0.0f; + break; + } + + doStep(id, +1, false); + g.stepAccumulator -= 1.0f; + + if (g.currentPos >= g.maxPos) { + g.currentPos = g.maxPos; + g.targetPos = g.maxPos; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + break; + } + } + + while (g.stepAccumulator <= -1.0f) { + if (g.currentPos == g.targetPos) { + g.stepAccumulator = 0.0f; + g.velocity = 0.0f; + break; + } + + doStep(id, -1, false); + g.stepAccumulator += 1.0f; + + if (g.currentPos <= g.minPos) { + g.currentPos = g.minPos; + g.targetPos = g.minPos; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + break; + } + } +} + +// Parses `SET ` and updates the target position. +// Replies: `OK`, `ERR BAD_ID`. +bool parseSet(const char* line) { + const char* args = nullptr; + long id; + long pos; + if (!commandArgs(line, "SET", args)) return false; + if (!parseIntegerToken(args, id) || !parseIntegerToken(args, pos)) return false; + + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + + Gauge& g = gauges[(uint8_t)id]; + pos = constrain(pos, g.minPos, g.maxPos); + g.targetPos = pos; + g.sweepEnabled = false; + sendReply(F("OK")); + return true; +} + +// Parses `SPEED ` and updates the max step rate. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_SPEED`. +bool parseSpeed(const char* line) { + const char* args = nullptr; + long id; + long speed; + if (!commandArgs(line, "SPEED", args)) return false; + if (!parseIntegerToken(args, id) || !parseIntegerToken(args, speed)) return false; + + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + if (speed <= 0) { + sendReply(F("ERR BAD_SPEED")); + return true; + } + + gauges[(uint8_t)id].maxSpeed = (uint32_t)speed; + sendReply(F("OK")); + return true; +} + +// Parses `ACCEL ` and updates the acceleration limit. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_ACCEL`. +bool parseAccel(const char* line) { + const char* args = nullptr; + long id; + long accel; + if (!commandArgs(line, "ACCEL", args)) return false; + if (!parseIntegerToken(args, id) || !parseIntegerToken(args, accel)) return false; + + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + if (accel <= 0) { + sendReply(F("ERR BAD_ACCEL")); + return true; + } + + gauges[(uint8_t)id].accel = (uint32_t)accel; + sendReply(F("OK")); + return true; +} + +// Parses `ENABLE <0|1>` and toggles the selected driver. +// Replies: `OK`, `ERR BAD_ID`. +bool parseEnable(const char* line) { + int id, en; + if (sscanf(line, "ENABLE %d %d", &id, &en) == 2) { + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + + setEnable(id, en != 0); + sendReply(F("OK")); + return true; + } + return false; +} + +// Parses `ZERO ` and declares the current position to be home. +// Replies: `OK`, `ERR BAD_ID`. +bool parseZero(const char* line) { + int id; + if (sscanf(line, "ZERO %d", &id) == 1) { + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + + Gauge& g = gauges[id]; + g.currentPos = 0; + g.targetPos = 0; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + g.homed = true; + g.sweepEnabled = false; + sendReply(F("OK")); + return true; + } + return false; +} + +// Parses `HOME ` or `HOMEALL` and kicks off the homing sequence. +// Replies: `OK`, `ERR BAD_ID`. Successful completion later emits debug line `HOMED `. +bool parseHome(const char* line) { + int id; + if (sscanf(line, "HOME %d", &id) == 1) { + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + + requestHome(id); + sendReply(F("OK")); + return true; + } + + if (strcmp(line, "HOMEALL") == 0) { + requestHomeAll(); + sendReply(F("OK")); + return true; + } + + return false; +} + +// Parses `SWEEP ` and enables or disables end-to-end motion. +// Replies: `OK`, `ERR BAD_ID`. +bool parseSweep(const char* line) { + const char* args = nullptr; + long id; + long accel; + long speed; + if (!commandArgs(line, "SWEEP", args)) return false; + if (!parseIntegerToken(args, id) || + !parseIntegerToken(args, accel) || + !parseIntegerToken(args, speed)) { + return false; + } + + if (id < 0 || id >= GAUGE_COUNT) { + sendReply(F("ERR BAD_ID")); + return true; + } + + Gauge& g = gauges[(uint8_t)id]; + + if (accel <= 0 || speed <= 0) { + g.sweepEnabled = false; + g.velocity = 0.0f; + g.stepAccumulator = 0.0f; + sendReply(F("OK")); + return true; + } + + g.accel = (uint32_t)accel; + g.maxSpeed = (uint32_t)speed; + g.sweepEnabled = true; + g.sweepTowardMax = true; + g.targetPos = g.maxPos; + sendReply(F("OK")); + return true; +} + +// Answers `POS?` with current motion state for every gauge. +// Emits one `POS ` line per gauge, +// then replies `OK`. +bool parsePosQuery(const char* line) { + if (strcmp(line, "POS?") == 0) { + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + CMD_PORT.print(F("POS ")); + CMD_PORT.print(i); + CMD_PORT.print(' '); + CMD_PORT.print(gauges[i].currentPos); + CMD_PORT.print(' '); + CMD_PORT.print(gauges[i].targetPos); + CMD_PORT.print(' '); + CMD_PORT.print(gauges[i].homed ? 1 : 0); + CMD_PORT.print(' '); + CMD_PORT.print((int)gauges[i].homingState); + CMD_PORT.print(' '); + CMD_PORT.println(gauges[i].sweepEnabled ? 1 : 0); + } + sendReply(F("OK")); + return true; + } + return false; +} + +// Answers `CFG?` with speed and acceleration for every gauge. +// Emits one `CFG ` line per gauge, then replies `OK`. +bool parseCfgQuery(const char* line) { + if (strcmp(line, "CFG?") == 0) { + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + CMD_PORT.print(F("CFG ")); + CMD_PORT.print(i); + CMD_PORT.print(' '); + CMD_PORT.print(gauges[i].maxSpeed); + CMD_PORT.print(' '); + CMD_PORT.println(gauges[i].accel); + } + sendReply(F("OK")); + return true; + } + return false; +} + +// Answers the mandatory life question: are you there? +// Reply: `PONG`. +bool parsePing(const char* line) { + if (strcmp(line, "PING") == 0) { + sendReply(F("PONG")); + return true; + } + return false; +} + +// Answers `LED?` with the current RGB values for every configured LED. +// Emits one `LED ` line per configured LED, then replies `OK`. +bool parseLedQuery(const char* line) { + if (strcmp(line, "LED?") == 0) { + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + for (uint8_t j = 0; j < gaugeLedCount[i]; j++) { + CRGB c = readLed(gaugeLedOffset[i] + j); + CMD_PORT.print(F("LED ")); + CMD_PORT.print(i); + CMD_PORT.print(' '); + CMD_PORT.print(j); + CMD_PORT.print(' '); + CMD_PORT.print(c.r); + CMD_PORT.print(' '); + CMD_PORT.print(c.g); + CMD_PORT.print(' '); + CMD_PORT.println(c.b); + } + } + sendReply(F("OK")); + return true; + } + return false; +} + +// Parses `LED ` and writes static colours. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_IDX`. +bool parseLed(const char* line) { + int id, r, g, b; + char idxToken[16]; + if (sscanf(line, "LED %d %15s %d %d %d", &id, idxToken, &r, &g, &b) == 5) { + if (id < 0 || id >= GAUGE_COUNT) { sendReply(F("ERR BAD_ID")); return true; } + char* dash = strchr(idxToken, '-'); + int idxFirst = atoi(idxToken); + int idxLast = dash ? atoi(dash + 1) : idxFirst; + if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) { + sendReply(F("ERR BAD_IDX")); return true; + } + CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255)); + for (int i = idxFirst; i <= idxLast; i++) { + blinkState[gaugeLedOffset[id] + i].active = false; + writeLed(gaugeLedOffset[id] + i, color); + } + sendReply(F("OK")); + return true; + } + return false; +} + +// Parses `BLINK ...` and assigns a simple on/off effect to one LED or a range. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_IDX`, `ERR BAD_TIME`. +bool parseBlink(const char* line) { + int id, onMs, offMs, r, g, b; + char idxToken[16]; + // Optional RGB values let BLINK either reuse or replace the current colour. + int count = sscanf(line, "BLINK %d %15s %d %d %d %d %d", + &id, idxToken, &onMs, &offMs, &r, &g, &b); + if (count != 4 && count != 7) return false; + + if (id < 0 || id >= GAUGE_COUNT) { sendReply(F("ERR BAD_ID")); return true; } + char* dash = strchr(idxToken, '-'); + int idxFirst = atoi(idxToken); + int idxLast = dash ? atoi(dash + 1) : idxFirst; + if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) { + sendReply(F("ERR BAD_IDX")); return true; + } + + if (onMs == 0 && offMs == 0) { + for (int i = idxFirst; i <= idxLast; i++) + blinkState[gaugeLedOffset[id] + i].active = false; + sendReply(F("OK")); + return true; + } + if (onMs <= 0 || offMs <= 0) { sendReply(F("ERR BAD_TIME")); return true; } + + CRGB color = (count == 7) + ? CRGB(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255)) + : CRGB(0, 0, 0); // Placeholder; replaced with the live LED colour below. + + unsigned long nowMs = millis(); + for (int i = idxFirst; i <= idxLast; i++) { + uint8_t globalIdx = gaugeLedOffset[id] + i; + BlinkState& bs = blinkState[globalIdx]; + bs.fx = FX_BLINK; + bs.onColor = (count == 7) ? color : readLed(globalIdx); + bs.onMs = (uint16_t)onMs; + bs.offMs = (uint16_t)offMs; + bs.currentlyOn = true; + bs.lastMs = nowMs; + bs.active = true; + writeLed(globalIdx, bs.onColor); + } + sendReply(F("OK")); + return true; +} + +// Parses `BREATHE ...` and assigns a triangle-wave fade effect. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_IDX`, `ERR BAD_TIME`. +bool parseBreathe(const char* line) { + int id, periodMs, r, g, b; + char idxToken[16]; + if (sscanf(line, "BREATHE %d %15s %d %d %d %d", + &id, idxToken, &periodMs, &r, &g, &b) != 6) return false; + if (id < 0 || id >= GAUGE_COUNT) { sendReply(F("ERR BAD_ID")); return true; } + char* dash = strchr(idxToken, '-'); + int idxFirst = atoi(idxToken); + int idxLast = dash ? atoi(dash + 1) : idxFirst; + if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) { + sendReply(F("ERR BAD_IDX")); return true; + } + if (periodMs <= 0) { sendReply(F("ERR BAD_TIME")); return true; } + CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255)); + unsigned long nowMs = millis(); + for (int i = idxFirst; i <= idxLast; i++) { + uint8_t gi = gaugeLedOffset[id] + i; + BlinkState& bs = blinkState[gi]; + bs.fx = FX_BREATHE; + bs.onColor = color; + bs.periodMs = (uint16_t)constrain(periodMs, 100, 30000); + bs.cyclePos = 0; + bs.lastMs = nowMs; + bs.active = true; + writeLed(gi, CRGB::Black); + } + sendReply(F("OK")); + return true; +} + +// Parses `DFLASH ...` and assigns the double-flash pattern. +// Replies: `OK`, `ERR BAD_ID`, `ERR BAD_IDX`. +bool parseDflash(const char* line) { + int id, r, g, b; + char idxToken[16]; + if (sscanf(line, "DFLASH %d %15s %d %d %d", + &id, idxToken, &r, &g, &b) != 5) return false; + if (id < 0 || id >= GAUGE_COUNT) { sendReply(F("ERR BAD_ID")); return true; } + char* dash = strchr(idxToken, '-'); + int idxFirst = atoi(idxToken); + int idxLast = dash ? atoi(dash + 1) : idxFirst; + if (idxFirst < 0 || idxLast >= gaugeLedCount[id] || idxFirst > idxLast) { + sendReply(F("ERR BAD_IDX")); return true; + } + CRGB color(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255)); + unsigned long nowMs = millis(); + for (int i = idxFirst; i <= idxLast; i++) { + uint8_t gi = gaugeLedOffset[id] + i; + BlinkState& bs = blinkState[gi]; + bs.fx = FX_DFLASH; + bs.onColor = color; + bs.dphase = 0; + bs.lastMs = nowMs; + bs.active = true; + writeLed(gi, color); // phase 0 = on + } + sendReply(F("OK")); + return true; +} + +// Advances all active LED effects. writeLed() marks the affected physical strip dirty. +void updateBlink() { + unsigned long nowMs = millis(); + + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + for (uint8_t j = 0; j < gaugeLedCount[i]; j++) { + uint8_t gi = gaugeLedOffset[i] + j; + BlinkState& bs = blinkState[gi]; + if (!bs.active) continue; + + switch (bs.fx) { + case FX_BLINK: { + uint32_t period = bs.currentlyOn ? bs.onMs : bs.offMs; + if ((nowMs - bs.lastMs) >= period) { + bs.currentlyOn = !bs.currentlyOn; + bs.lastMs = nowMs; + writeLed(gi, bs.currentlyOn ? bs.onColor : CRGB::Black); + } + break; + } + case FX_BREATHE: { + unsigned long dt = nowMs - bs.lastMs; + if (dt < BREATHE_FRAME_MS) break; + uint32_t newPos = (uint32_t)bs.cyclePos + dt; + bs.cyclePos = (uint16_t)(newPos % bs.periodMs); + bs.lastMs = nowMs; + // Triangle wave brightness; frame-limited so breathe remains smooth + // without refreshing the LED strips on every service-loop pass. + uint16_t half = bs.periodMs >> 1; + uint8_t bri = (bs.cyclePos < half) + ? (uint8_t)((uint32_t)bs.cyclePos * 255 / half) + : (uint8_t)((uint32_t)(bs.periodMs - bs.cyclePos) * 255 / half); + CRGB scaled = bs.onColor; + scaled.nscale8(bri ? bri : 1); + writeLed(gi, scaled); + break; + } + case FX_DFLASH: { + static const uint16_t dur[4] = {100, 100, 100, 700}; // on, off, on, longer off + if ((nowMs - bs.lastMs) >= dur[bs.dphase]) { + bs.lastMs = nowMs; + bs.dphase = (bs.dphase + 1) & 3; + writeLed(gi, (bs.dphase == 0 || bs.dphase == 2) ? bs.onColor : CRGB::Black); + } + break; + } + } + } + } +} + +// Runs the command parsers in order until one claims the line. +// Reply: `ERR BAD_CMD` when no parser accepts the line. +void processLine(const char* line) { + if (parseSet(line)) return; + if (parseSpeed(line)) return; + if (parseAccel(line)) return; + if (parseEnable(line)) return; + if (parseZero(line)) return; + if (parseHome(line)) return; + if (parseSweep(line)) return; + if (parsePosQuery(line)) return; + if (parseCfgQuery(line)) return; + if (parseLedQuery(line)) return; + if (parseLed(line)) return; + if (parseBlink(line)) return; + if (parseBreathe(line)) return; + if (parseDflash(line)) return; + if (parsePing(line)) return; + + sendReply(F("ERR BAD_CMD")); +} + +// Reads newline-delimited commands from serial and hands complete lines to the parser. +// Reply: `ERR TOO_LONG` when the buffered line exceeds the receive limit before newline. +void readCommands() { + while (CMD_PORT.available()) { + char c = (char)CMD_PORT.read(); + + if (c == '\n') { + if (rxOverflowed) { + rxOverflowed = false; + rxLen = 0; + continue; + } + + rxLine[rxLen] = '\0'; + char* line = trimLine(rxLine); + if (*line != '\0') { + processLine(line); + } + rxLen = 0; + } else if (c != '\r' && !rxOverflowed) { + if (rxLen < RX_LINE_MAX) { + rxLine[rxLen++] = c; + } else { + rxLen = 0; + rxOverflowed = true; + sendReply(F("ERR TOO_LONG")); + } + } + } +} + +// Initialises pins, LED bookkeeping and the initial homing cycle. +// Reply/event: emits `READY` on CMD_PORT once boot is complete. +void setup() { + DEBUG_PORT.begin(SERIAL_BAUD); + DEBUG_PORT.println(F("Gauge controller booting")); + + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + pinMode(gaugePins[i].dirPin, OUTPUT); + pinMode(gaugePins[i].stepPin, OUTPUT); + + digitalWrite(gaugePins[i].dirPin, LOW); + digitalWrite(gaugePins[i].stepPin, gaugePins[i].stepActiveHigh ? LOW : HIGH); + + if (gaugePins[i].enablePin >= 0) { + pinMode(gaugePins[i].enablePin, OUTPUT); + setEnable(i, true); + } + + gauges[i].lastUpdateMicros = micros(); + } + + // Flatten the per-gauge LED counts into logical offsets and separate + // physical offsets for the main and indicator strips. + uint8_t ledOff = 0; + uint8_t mainLedOff = 0; + uint8_t indicatorLedOff = 0; + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + gaugeLedCount[i] = cstrLen(gaugePins[i].ledOrder); + gaugeLedOffset[i] = ledOff; + gaugeMainLedOffset[i] = mainLedOff; + gaugeIndicatorLedOffset[i] = indicatorLedOff; + + for (uint8_t localIdx = 0; localIdx < gaugeLedCount[i]; localIdx++) { + uint8_t globalIdx = ledOff + localIdx; + bool indicator = isIndicatorLedIndex(localIdx); + ledIsIndicator[globalIdx] = indicator; + ledRgSwap[globalIdx] = gaugePins[i].ledOrder[localIdx] == 'G' || + gaugePins[i].ledOrder[localIdx] == 'g'; + ledPhysicalIdx[globalIdx] = indicator + ? indicatorLedOff + (localIdx - 3) + : mainLedOff + localIdx - (localIdx > 4 ? 2 : 0); + } + + ledOff += gaugeLedCount[i]; + indicatorLedOff += countIndicatorLedsForGauge(i); + mainLedOff += gaugeLedCount[i] - countIndicatorLedsForGauge(i); + } + mainLedController = &FastLED.addLeds(mainLeds, TOTAL_MAIN_LEDS); + indicatorLedController = &FastLED.addLeds(indicatorLeds, TOTAL_INDICATOR_LEDS); + FastLED.setBrightness(255); + mainLedController->showLeds(255); + indicatorLedController->showLeds(255); + + requestHomeAll(); + + DEBUG_PORT.println(F("READY")); + // Boot-complete handshake for the command channel. + sendReply(F("READY")); +} + +// Main service loop: ingest commands, advance effects, move gauges, flush LEDs. +void loop() { + readCommands(); + updateBlink(); + + unsigned long nowUs = micros(); + unsigned long nowMs = millis(); + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { + updateGauge(i, nowUs, nowMs); + } + + showDirtyLeds(); + + +} diff --git a/Gaugecontroller_no_VFD/esp-home_no_VFD.yaml b/Gaugecontroller_no_VFD/esp-home_no_VFD.yaml new file mode 100644 index 0000000..d4d9f5c --- /dev/null +++ b/Gaugecontroller_no_VFD/esp-home_no_VFD.yaml @@ -0,0 +1,2395 @@ +substitutions: + # --- Device identity --- + device_name: gaugecontroller + device_friendly_name: Selsyn Multi + device_area: Control Panels + + # --- WiFi --- + wifi_ssid: !secret wifi_ssid + wifi_password: !secret wifi_password + + # --- Arduino UART bridge --- + arduino_tx_pin: GPIO4 + arduino_rx_pin: GPIO5 + arduino_baud: "38400" + + # --- Gauge 1 --- + gauge0_name: Selsyn 1 + gauge0_entity_name: Selsyn 1 Power + gauge0_min: "0.0" + gauge0_max: "7612.0" + gauge0_max_steps: "3780" + gauge0_unit: W + gauge0_step: "1.0" + gauge0_speed_step: "1" + gauge0_accel_step: "1" + gauge0_default_speed: "2000" + gauge0_default_accel: "6000" + + # --- Gauge 2 --- + gauge1_name: Selsyn 2 + gauge1_entity_name: Selsyn 2 Temperature + gauge1_min: "0.0" + gauge1_max: "76.0" + gauge1_max_steps: "3780" + gauge1_unit: Deg + gauge1_step: "1.0" + gauge1_speed_step: "1" + gauge1_accel_step: "1" + gauge1_default_speed: "2000" + gauge1_default_accel: "6000" + + # --- Gauge 3 --- + gauge2_name: Selsyn 3 + gauge2_entity_name: Selsyn 3 Power + gauge2_min: "0.0" + gauge2_max: "7612.0" + gauge2_max_steps: "3780" + gauge2_unit: W + gauge2_step: "1.0" + gauge2_speed_step: "1" + gauge2_accel_step: "1" + gauge2_default_speed: "2000" + gauge2_default_accel: "6000" + + # --- Gauge 4 --- + gauge3_name: Selsyn 4 + gauge3_entity_name: Selsyn 4 Power + gauge3_min: "0.0" + gauge3_max: "7612.0" + gauge3_max_steps: "3780" + gauge3_unit: W + gauge3_step: "1.0" + gauge3_speed_step: "1" + gauge3_accel_step: "1" + gauge3_default_speed: "2000" + gauge3_default_accel: "6000" + + # --- Timing --- + rezero_interval: 3600s + +# --------------------------------------------------------------------------- +# Core ESPHome identity +# --------------------------------------------------------------------------- + +esphome: + name: ${device_name} + friendly_name: ${device_friendly_name} + on_boot: + priority: -100 + then: + - delay: 2s + - uart.write: + id: arduino_uart + data: "HOMEALL\n" + - logger.log: "Boot: HOMEALL sent to Arduino" + - delay: 15s + - uart.write: + id: arduino_uart + data: "POS?\n" + - logger.log: "Boot: POS? sent to Arduino" + - uart.write: + id: arduino_uart + data: "CFG?\n" + - logger.log: "Boot: CFG? sent to Arduino" + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + version: recommended + +# --------------------------------------------------------------------------- +# Logging (set level: NONE in production) +# --------------------------------------------------------------------------- + +logger: + level: DEBUG + +# --------------------------------------------------------------------------- +# Native HA API — handles all entity registration, state sync, and commands. +# No manual discovery payloads needed. +# --------------------------------------------------------------------------- + +api: + +# --------------------------------------------------------------------------- +# OTA — replaces ota.py / Gitea updater; managed from HA or esphome CLI +# --------------------------------------------------------------------------- + +ota: + - platform: esphome + +# --------------------------------------------------------------------------- +# WiFi — replaces connect_wifi() + check_wifi() + all reconnect logic +# --------------------------------------------------------------------------- + +wifi: + ssid: ${wifi_ssid} + password: ${wifi_password} + output_power: 15 + ap: + ssid: "${device_name} Fallback" + password: !secret fallbackpassword + +captive_portal: + +# --------------------------------------------------------------------------- +# Arduino UART bridge +# --------------------------------------------------------------------------- + +uart: + id: arduino_uart + tx_pin: ${arduino_tx_pin} + rx_pin: ${arduino_rx_pin} + baud_rate: ${arduino_baud} + # Uncomment to log raw Arduino traffic: + # debug: + # direction: BOTH + +# --------------------------------------------------------------------------- +# Connectivity diagnostics +# --------------------------------------------------------------------------- + +sensor: + - platform: wifi_signal + name: WiFi Signal + update_interval: 60s + entity_category: diagnostic + + - platform: uptime + name: Uptime + entity_category: diagnostic + + - platform: template + name: "${gauge0_name} Target" + lambda: |- + return id(gauge0_target_value); + unit_of_measurement: "${gauge0_unit}" + accuracy_decimals: 1 + update_interval: 5s + + - platform: template + name: "${gauge1_name} Target" + lambda: |- + return id(gauge1_target_value); + unit_of_measurement: "${gauge1_unit}" + accuracy_decimals: 1 + update_interval: 5s + + - platform: template + name: "${gauge2_name} Target" + lambda: |- + return id(gauge2_target_value); + unit_of_measurement: "${gauge2_unit}" + accuracy_decimals: 1 + update_interval: 5s + + - platform: template + name: "${gauge3_name} Target" + lambda: |- + return id(gauge3_target_value); + unit_of_measurement: "${gauge3_unit}" + accuracy_decimals: 1 + update_interval: 5s + + entity_category: diagnostic + +text_sensor: + - platform: wifi_info + ip_address: + name: IP Address + entity_category: diagnostic + ssid: + name: SSID + entity_category: diagnostic + + - platform: template + id: arduino_last_message + name: Arduino Last Message + entity_category: diagnostic + +# --------------------------------------------------------------------------- +# Gauge target numbers +# --------------------------------------------------------------------------- + +number: + - platform: template + id: gauge0_target_number + name: "${gauge0_entity_name}" + unit_of_measurement: "${gauge0_unit}" + min_value: ${gauge0_min} + max_value: ${gauge0_max} + step: ${gauge0_step} + mode: slider + optimistic: true + restore_value: false + initial_value: ${gauge0_min} + set_action: + - lambda: |- + const float min_value = ${gauge0_min}; + const float max_value = ${gauge0_max}; + const int max_steps = ${gauge0_max_steps}; + const float clamped = std::max(min_value, std::min(max_value, x)); + int steps = 0; + if (max_value > min_value) { + const float fraction = (clamped - min_value) / (max_value - min_value); + steps = static_cast(lroundf(fraction * max_steps)); + } + id(gauge0_target_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SET 0 %d\n", steps); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge1_target_number + name: "${gauge1_entity_name}" + unit_of_measurement: "${gauge1_unit}" + min_value: ${gauge1_min} + max_value: ${gauge1_max} + step: ${gauge1_step} + mode: slider + optimistic: true + restore_value: false + initial_value: ${gauge1_min} + set_action: + - lambda: |- + const float min_value = ${gauge1_min}; + const float max_value = ${gauge1_max}; + const int max_steps = ${gauge1_max_steps}; + const float clamped = std::max(min_value, std::min(max_value, x)); + int steps = 0; + if (max_value > min_value) { + const float fraction = (clamped - min_value) / (max_value - min_value); + steps = static_cast(lroundf(fraction * max_steps)); + } + id(gauge1_target_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SET 1 %d\n", steps); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge2_target_number + name: "${gauge2_entity_name}" + unit_of_measurement: "${gauge2_unit}" + min_value: ${gauge2_min} + max_value: ${gauge2_max} + step: ${gauge2_step} + mode: slider + optimistic: true + restore_value: false + initial_value: ${gauge2_min} + set_action: + - lambda: |- + const float min_value = ${gauge2_min}; + const float max_value = ${gauge2_max}; + const int max_steps = ${gauge2_max_steps}; + const float clamped = std::max(min_value, std::min(max_value, x)); + int steps = 0; + if (max_value > min_value) { + const float fraction = (clamped - min_value) / (max_value - min_value); + steps = static_cast(lroundf(fraction * max_steps)); + } + id(gauge2_target_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SET 2 %d\n", steps); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge3_target_number + name: "${gauge3_entity_name}" + unit_of_measurement: "${gauge3_unit}" + min_value: ${gauge3_min} + max_value: ${gauge3_max} + step: ${gauge3_step} + mode: slider + optimistic: true + restore_value: false + initial_value: ${gauge3_min} + set_action: + - lambda: |- + const float min_value = ${gauge3_min}; + const float max_value = ${gauge3_max}; + const int max_steps = ${gauge3_max_steps}; + const float clamped = std::max(min_value, std::min(max_value, x)); + int steps = 0; + if (max_value > min_value) { + const float fraction = (clamped - min_value) / (max_value - min_value); + steps = static_cast(lroundf(fraction * max_steps)); + } + id(gauge3_target_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SET 3 %d\n", steps); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge0_speed_number + name: "${gauge0_name} Speed" + entity_category: diagnostic + unit_of_measurement: "steps/s" + min_value: 1 + max_value: 20000 + step: ${gauge0_speed_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge0_default_speed} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge0_speed_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SPEED 0 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge0_accel_number + name: "${gauge0_name} Acceleration" + entity_category: diagnostic + unit_of_measurement: "steps/s^2" + min_value: 1 + max_value: 50000 + step: ${gauge0_accel_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge0_default_accel} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge0_accel_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "ACCEL 0 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge1_speed_number + name: "${gauge1_name} Speed" + entity_category: diagnostic + unit_of_measurement: "steps/s" + min_value: 1 + max_value: 20000 + step: ${gauge1_speed_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge1_default_speed} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge1_speed_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SPEED 1 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge1_accel_number + name: "${gauge1_name} Acceleration" + entity_category: diagnostic + unit_of_measurement: "steps/s^2" + min_value: 1 + max_value: 50000 + step: ${gauge1_accel_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge1_default_accel} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge1_accel_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "ACCEL 1 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge2_speed_number + name: "${gauge2_name} Speed" + entity_category: diagnostic + unit_of_measurement: "steps/s" + min_value: 1 + max_value: 20000 + step: ${gauge2_speed_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge2_default_speed} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge2_speed_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SPEED 2 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge2_accel_number + name: "${gauge2_name} Acceleration" + entity_category: diagnostic + unit_of_measurement: "steps/s^2" + min_value: 1 + max_value: 50000 + step: ${gauge2_accel_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge2_default_accel} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge2_accel_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "ACCEL 2 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge3_speed_number + name: "${gauge3_name} Speed" + entity_category: diagnostic + unit_of_measurement: "steps/s" + min_value: 1 + max_value: 20000 + step: ${gauge3_speed_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge3_default_speed} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge3_speed_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "SPEED 3 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: template + id: gauge3_accel_number + name: "${gauge3_name} Acceleration" + entity_category: diagnostic + unit_of_measurement: "steps/s^2" + min_value: 1 + max_value: 50000 + step: ${gauge3_accel_step} + mode: box + optimistic: true + restore_value: false + initial_value: ${gauge3_default_accel} + set_action: + - lambda: |- + const int clamped = std::max(1, static_cast(lroundf(x))); + id(gauge3_accel_value) = clamped; + char cmd[24]; + snprintf(cmd, sizeof(cmd), "ACCEL 3 %d\n", clamped); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + +# --------------------------------------------------------------------------- +# VFD controls +# --------------------------------------------------------------------------- +# +#text: +# - platform: template +# id: vfd_display +# name: VFD Display +# mode: text +# optimistic: true +# restore_value: false +# min_length: 0 +# max_length: 4 +# initial_value: "" +# set_action: +# - lambda: |- +# std::string sanitized; +# sanitized.reserve(4); +# for (char c : x) { +# const char uc = static_cast(toupper(static_cast(c))); +# if ((uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'F') || uc == '-') { +# sanitized.push_back(uc); +# if (sanitized.size() >= 4) { +# break; +# } +# } +# } +# +# id(vfd_text_value) = sanitized; +# +# std::string cmd = "VFD"; +# std::string suffix; +# if (id(vfd_decimal_point_on)) { +# suffix += "."; +# } +# if (id(vfd_alarm_on)) { +# suffix += "!"; +# } +# +# if (!sanitized.empty()) { +# cmd += " " + sanitized + suffix; +# } else if (!suffix.empty()) { +# cmd += " 0" + suffix; +# } +# cmd += "\n"; +# ESP_LOGI("uart_tx", "TX: %s", cmd.c_str()); +# id(arduino_uart).write_str(cmd.c_str()); +# +#switch: +# - platform: template +# id: vfd_decimal_point +# name: VFD Decimal Point +# optimistic: true +# restore_mode: ALWAYS_OFF +# lambda: |- +# return id(vfd_decimal_point_on); +# turn_on_action: +# - lambda: |- +# id(vfd_decimal_point_on) = true; +# std::string cmd = "VFD"; +# std::string suffix = "."; +# if (id(vfd_alarm_on)) { +# suffix += "!"; +# } +# if (!id(vfd_text_value).empty()) { +# cmd += " " + id(vfd_text_value) + suffix; +# } else { +# cmd += " 0" + suffix; +# } +# cmd += "\n"; +# ESP_LOGI("uart_tx", "TX: %s", cmd.c_str()); +# id(arduino_uart).write_str(cmd.c_str()); +# turn_off_action: +# - lambda: |- +# id(vfd_decimal_point_on) = false; +# std::string cmd = "VFD"; +# std::string suffix; +# if (id(vfd_alarm_on)) { +# suffix += "!"; +# } +# if (!id(vfd_text_value).empty()) { +# cmd += " " + id(vfd_text_value) + suffix; +# } else if (!suffix.empty()) { +# cmd += " 0" + suffix; +# } +# cmd += "\n"; +# ESP_LOGI("uart_tx", "TX: %s", cmd.c_str()); +# id(arduino_uart).write_str(cmd.c_str()); +# +# - platform: template +# id: vfd_alarm +# name: VFD Alarm +# optimistic: true +# restore_mode: ALWAYS_OFF +# lambda: |- +# return id(vfd_alarm_on); +# turn_on_action: +# - lambda: |- +# id(vfd_alarm_on) = true; +# std::string cmd = "VFD"; +# std::string suffix; +# if (id(vfd_decimal_point_on)) { +# suffix += "."; +# } +# suffix += "!"; +# if (!id(vfd_text_value).empty()) { +# cmd += " " + id(vfd_text_value) + suffix; +# } else { +# cmd += " 0" + suffix; +# } +# cmd += "\n"; +# ESP_LOGI("uart_tx", "TX: %s", cmd.c_str()); +# id(arduino_uart).write_str(cmd.c_str()); +# turn_off_action: +# - lambda: |- +# id(vfd_alarm_on) = false; +# std::string cmd = "VFD"; +# std::string suffix; +# if (id(vfd_decimal_point_on)) { +# suffix += "."; +# } +# if (!id(vfd_text_value).empty()) { +# cmd += " " + id(vfd_text_value) + suffix; +# } else if (!suffix.empty()) { +# cmd += " 0" + suffix; +# } +# cmd += "\n"; +# ESP_LOGI("uart_tx", "TX: %s", cmd.c_str()); +# id(arduino_uart).write_str(cmd.c_str()); +# +# --------------------------------------------------------------------------- +# Gauge homing buttons +# --------------------------------------------------------------------------- + +button: + - platform: template + name: "${gauge0_name} Rezero" + entity_category: diagnostic + on_press: + - uart.write: + id: arduino_uart + data: "HOME 0\n" + + - platform: template + name: "${gauge1_name} Rezero" + entity_category: diagnostic + on_press: + - uart.write: + id: arduino_uart + data: "HOME 1\n" + + - platform: template + name: "${gauge2_name} Rezero" + entity_category: diagnostic + on_press: + - uart.write: + id: arduino_uart + data: "HOME 2\n" + + - platform: template + name: "${gauge3_name} Rezero" + entity_category: diagnostic + on_press: + - uart.write: + id: arduino_uart + data: "HOME 3\n" + + - platform: template + name: Rezero All Gauges + entity_category: diagnostic + on_press: + - uart.write: + id: arduino_uart + data: "HOMEALL\n" + +# --------------------------------------------------------------------------- +# Periodic auto-rezero +# --------------------------------------------------------------------------- + +interval: + - interval: 10ms + then: + - lambda: |- + while (id(arduino_uart).available()) { + uint8_t b; + id(arduino_uart).read_byte(&b); + if (b == '\n') { + std::string &buf = id(uart_rx_buf); + if (!buf.empty() && buf.back() == '\r') buf.pop_back(); + if (!buf.empty()) ESP_LOGI("uart_rx", "RX: %s", buf.c_str()); + if (!buf.empty() && buf != "OK" && buf != "PONG") { + if (buf.rfind("POS ", 0) == 0) { + int gid, cur, tgt, homed, hstate, sweep; + if (sscanf(buf.c_str(), "POS %d %d %d %d %d %d", + &gid, &cur, &tgt, &homed, &hstate, &sweep) == 6) { + float value = 0.0f; + if (gid == 0) { + value = ${gauge0_min} + (tgt / (float)${gauge0_max_steps}) * (${gauge0_max} - ${gauge0_min}); + id(gauge0_target_value) = value; + id(gauge0_target_number).publish_state(value); + } else if (gid == 1) { + value = ${gauge1_min} + (tgt / (float)${gauge1_max_steps}) * (${gauge1_max} - ${gauge1_min}); + id(gauge1_target_value) = value; + id(gauge1_target_number).publish_state(value); + } else if (gid == 2) { + value = ${gauge2_min} + (tgt / (float)${gauge2_max_steps}) * (${gauge2_max} - ${gauge2_min}); + id(gauge2_target_value) = value; + id(gauge2_target_number).publish_state(value); + } else if (gid == 3) { + value = ${gauge3_min} + (tgt / (float)${gauge3_max_steps}) * (${gauge3_max} - ${gauge3_min}); + id(gauge3_target_value) = value; + id(gauge3_target_number).publish_state(value); + } + } + } else if (buf.rfind("CFG ", 0) == 0) { + int gid, spd, acc; + if (sscanf(buf.c_str(), "CFG %d %d %d", &gid, &spd, &acc) == 3) { + if (gid == 0) { + id(gauge0_speed_value) = spd; + id(gauge0_accel_value) = acc; + id(gauge0_speed_number).publish_state(spd); + id(gauge0_accel_number).publish_state(acc); + } else if (gid == 1) { + id(gauge1_speed_value) = spd; + id(gauge1_accel_value) = acc; + id(gauge1_speed_number).publish_state(spd); + id(gauge1_accel_number).publish_state(acc); + } else if (gid == 2) { + id(gauge2_speed_value) = spd; + id(gauge2_accel_value) = acc; + id(gauge2_speed_number).publish_state(spd); + id(gauge2_accel_number).publish_state(acc); + } else if (gid == 3) { + id(gauge3_speed_value) = spd; + id(gauge3_accel_value) = acc; + id(gauge3_speed_number).publish_state(spd); + id(gauge3_accel_number).publish_state(acc); + } + } + } else { + id(arduino_last_message).publish_state(buf); + } + } + buf.clear(); + } else if (id(uart_rx_buf).size() < 128) { + id(uart_rx_buf) += static_cast(b); + } + } + - interval: 30s + then: + - uart.write: + id: arduino_uart + data: "POS?\n" + - uart.write: + id: arduino_uart + data: "CFG?\n" + - interval: ${rezero_interval} + then: + - uart.write: + id: arduino_uart + data: "HOMEALL\n" + +# --------------------------------------------------------------------------- +# Gauge light state cache +# --------------------------------------------------------------------------- + +globals: + - id: uart_rx_buf + type: std::string + restore_value: no + initial_value: '""' +# - id: vfd_text_value +# type: std::string +# restore_value: no +# initial_value: '""' +# - id: vfd_decimal_point_on +# type: bool +# restore_value: no +# initial_value: "false" +# - id: vfd_alarm_on +# type: bool +# restore_value: no +# initial_value: "false" + - id: gauge0_target_value + type: float + restore_value: no + initial_value: ${gauge0_min} + - id: gauge1_target_value + type: float + restore_value: no + initial_value: ${gauge1_min} + - id: gauge2_target_value + type: float + restore_value: no + initial_value: ${gauge2_min} + - id: gauge3_target_value + type: float + restore_value: no + initial_value: ${gauge3_min} + - id: gauge0_speed_value + type: int + restore_value: no + initial_value: ${gauge0_default_speed} + - id: gauge0_accel_value + type: int + restore_value: no + initial_value: ${gauge0_default_accel} + - id: gauge1_speed_value + type: int + restore_value: no + initial_value: ${gauge1_default_speed} + - id: gauge1_accel_value + type: int + restore_value: no + initial_value: ${gauge1_default_accel} + - id: gauge2_speed_value + type: int + restore_value: no + initial_value: ${gauge2_default_speed} + - id: gauge2_accel_value + type: int + restore_value: no + initial_value: ${gauge2_default_accel} + - id: gauge3_speed_value + type: int + restore_value: no + initial_value: ${gauge3_default_speed} + - id: gauge3_accel_value + type: int + restore_value: no + initial_value: ${gauge3_default_accel} + - id: gauge0_bl_r + type: int + restore_value: no + initial_value: "0" + - id: gauge0_bl_g + type: int + restore_value: no + initial_value: "0" + - id: gauge0_bl_b + type: int + restore_value: no + initial_value: "0" + - id: gauge1_bl_r + type: int + restore_value: no + initial_value: "0" + - id: gauge1_bl_g + type: int + restore_value: no + initial_value: "0" + - id: gauge1_bl_b + type: int + restore_value: no + initial_value: "0" + - id: gauge2_bl_r + type: int + restore_value: no + initial_value: "0" + - id: gauge2_bl_g + type: int + restore_value: no + initial_value: "0" + - id: gauge2_bl_b + type: int + restore_value: no + initial_value: "0" + - id: gauge3_bl_r + type: int + restore_value: no + initial_value: "0" + - id: gauge3_bl_g + type: int + restore_value: no + initial_value: "0" + - id: gauge3_bl_b + type: int + restore_value: no + initial_value: "0" + - id: gauge0_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge0_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge0_status_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge0_status_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge1_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge1_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge1_status_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge1_status_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge2_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge2_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge2_status_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge2_status_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge3_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge3_green_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge3_status_red_on + type: bool + restore_value: no + initial_value: "false" + - id: gauge3_status_green_on + type: bool + restore_value: no + initial_value: "false" + +# --------------------------------------------------------------------------- +# Light outputs +# --------------------------------------------------------------------------- + +output: + - platform: template + id: gauge0_backlight_red_output + type: float + write_action: + - lambda: |- + id(gauge0_bl_r) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge0_backlight_green_output + type: float + write_action: + - lambda: |- + id(gauge0_bl_g) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge0_backlight_blue_output + type: float + write_action: + - lambda: |- + id(gauge0_bl_b) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge1_backlight_red_output + type: float + write_action: + - lambda: |- + id(gauge1_bl_r) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge1_backlight_green_output + type: float + write_action: + - lambda: |- + id(gauge1_bl_g) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge1_backlight_blue_output + type: float + write_action: + - lambda: |- + id(gauge1_bl_b) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge2_backlight_red_output + type: float + write_action: + - lambda: |- + id(gauge2_bl_r) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge2_backlight_green_output + type: float + write_action: + - lambda: |- + id(gauge2_bl_g) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge2_backlight_blue_output + type: float + write_action: + - lambda: |- + id(gauge2_bl_b) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge3_backlight_red_output + type: float + write_action: + - lambda: |- + id(gauge3_bl_r) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge3_backlight_green_output + type: float + write_action: + - lambda: |- + id(gauge3_bl_g) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge3_backlight_blue_output + type: float + write_action: + - lambda: |- + id(gauge3_bl_b) = static_cast(state * 255.0f + 0.5f); + + - platform: template + id: gauge0_red_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge0_red_on) = state; + + - platform: template + id: gauge0_green_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge0_green_on) = state; + + - platform: template + id: gauge0_status_red_output + type: binary + write_action: + - lambda: |- + id(gauge0_status_red_on) = state; + + - platform: template + id: gauge0_status_green_output + type: binary + write_action: + - lambda: |- + id(gauge0_status_green_on) = state; + + - platform: template + id: gauge1_red_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge1_red_on) = state; + + - platform: template + id: gauge1_green_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge1_green_on) = state; + + - platform: template + id: gauge1_status_red_output + type: binary + write_action: + - lambda: |- + id(gauge1_status_red_on) = state; + + - platform: template + id: gauge1_status_green_output + type: binary + write_action: + - lambda: |- + id(gauge1_status_green_on) = state; + + - platform: template + id: gauge2_red_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge2_red_on) = state; + + - platform: template + id: gauge2_green_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge2_green_on) = state; + + - platform: template + id: gauge2_status_red_output + type: binary + write_action: + - lambda: |- + id(gauge2_status_red_on) = state; + + - platform: template + id: gauge2_status_green_output + type: binary + write_action: + - lambda: |- + id(gauge2_status_green_on) = state; + + - platform: template + id: gauge3_red_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge3_red_on) = state; + + - platform: template + id: gauge3_green_indicator_output + type: binary + write_action: + - lambda: |- + id(gauge3_green_on) = state; + + - platform: template + id: gauge3_status_red_output + type: binary + write_action: + - lambda: |- + id(gauge3_status_red_on) = state; + + - platform: template + id: gauge3_status_green_output + type: binary + write_action: + - lambda: |- + id(gauge3_status_green_on) = state; + +# --------------------------------------------------------------------------- +# Native HA lights +# --------------------------------------------------------------------------- + +light: + - platform: rgb + id: gauge0_backlight + name: "${gauge0_name} Backlight" + red: gauge0_backlight_red_output + green: gauge0_backlight_green_output + blue: gauge0_backlight_blue_output + default_transition_length: 0s + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 0 0-2 800 800 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 0 0-2 150 150 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 0 0-2 100 400 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 0 0-2 3000 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 0 0-2 1200 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + char cmd[32]; + snprintf(cmd, sizeof(cmd), "DFLASH 0 0-2 %d %d %d\n", + id(gauge0_bl_r), id(gauge0_bl_g), id(gauge0_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + on_state: + - lambda: |- + auto effect = id(gauge0_backlight).get_effect_name(); + if (effect != "None" && !effect.empty()) return; + auto& rv = id(gauge0_backlight).remote_values; + char cmd[32]; + snprintf(cmd, sizeof(cmd), "LED 0 0-2 %d %d %d\n", + rv.is_on() ? static_cast(rv.get_red() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_green() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_blue() * rv.get_brightness() * 255.0f + 0.5f) : 0); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: binary + id: gauge0_red_indicator + name: "${gauge0_name} Red Indicator" + output: gauge0_red_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 0 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge0_red_indicator).remote_values.is_on()) { + { const char* cmd = "LED 0 3 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge0_red_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 0 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 0 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 0 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 0 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 0 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 0 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 0 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge0_green_indicator + name: "${gauge0_name} Green Indicator" + output: gauge0_green_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 0 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge0_green_indicator).remote_values.is_on()) { + { const char* cmd = "LED 0 4 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge0_green_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 0 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 0 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 0 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 0 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 0 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 0 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 0 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge0_status_red + name: "${gauge0_name} Status Red" + output: gauge0_status_red_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 0 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge0_status_red).remote_values.is_on()) { + { const char* cmd = "LED 0 5 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge0_status_red).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 0 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 0 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 0 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 0 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 0 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 0 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 0 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge0_status_green + name: "${gauge0_name} Status Green" + output: gauge0_status_green_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 0 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 0 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 0 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge0_status_green).remote_values.is_on()) { + { const char* cmd = "LED 0 6 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge0_status_green).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 0 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 0 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 0 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 0 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 0 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 0 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 0 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: rgb + id: gauge1_backlight + name: "${gauge1_name} Backlight" + red: gauge1_backlight_red_output + green: gauge1_backlight_green_output + blue: gauge1_backlight_blue_output + default_transition_length: 0s + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 1 0-2 800 800 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 1 0-2 150 150 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 1 0-2 100 400 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 1 0-2 3000 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 1 0-2 1200 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + char cmd[32]; + snprintf(cmd, sizeof(cmd), "DFLASH 1 0-2 %d %d %d\n", + id(gauge1_bl_r), id(gauge1_bl_g), id(gauge1_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + on_state: + - lambda: |- + auto effect = id(gauge1_backlight).get_effect_name(); + if (effect != "None" && !effect.empty()) return; + auto& rv = id(gauge1_backlight).remote_values; + char cmd[32]; + snprintf(cmd, sizeof(cmd), "LED 1 0-2 %d %d %d\n", + rv.is_on() ? static_cast(rv.get_red() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_green() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_blue() * rv.get_brightness() * 255.0f + 0.5f) : 0); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: binary + id: gauge1_red_indicator + name: "${gauge1_name} Red Indicator" + output: gauge1_red_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 1 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge1_red_indicator).remote_values.is_on()) { + { const char* cmd = "LED 1 3 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge1_red_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 1 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 1 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 1 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 1 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 1 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 1 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 1 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge1_green_indicator + name: "${gauge1_name} Green Indicator" + output: gauge1_green_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 1 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge1_green_indicator).remote_values.is_on()) { + { const char* cmd = "LED 1 4 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge1_green_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 1 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 1 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 1 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 1 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 1 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 1 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 1 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge1_status_red + name: "${gauge1_name} Status Red" + output: gauge1_status_red_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 1 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge1_status_red).remote_values.is_on()) { + { const char* cmd = "LED 1 5 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge1_status_red).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 1 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 1 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 1 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 1 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 1 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 1 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 1 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge1_status_green + name: "${gauge1_name} Status Green" + output: gauge1_status_green_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 1 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 1 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 1 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge1_status_green).remote_values.is_on()) { + { const char* cmd = "LED 1 6 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge1_status_green).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 1 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 1 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 1 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 1 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 1 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 1 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 1 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: rgb + id: gauge2_backlight + name: "${gauge2_name} Backlight" + red: gauge2_backlight_red_output + green: gauge2_backlight_green_output + blue: gauge2_backlight_blue_output + default_transition_length: 0s + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 2 0-2 800 800 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 2 0-2 150 150 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 2 0-2 100 400 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 2 0-2 3000 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 2 0-2 1200 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + char cmd[32]; + snprintf(cmd, sizeof(cmd), "DFLASH 2 0-2 %d %d %d\n", + id(gauge2_bl_r), id(gauge2_bl_g), id(gauge2_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + on_state: + - lambda: |- + auto effect = id(gauge2_backlight).get_effect_name(); + if (effect != "None" && !effect.empty()) return; + auto& rv = id(gauge2_backlight).remote_values; + char cmd[32]; + snprintf(cmd, sizeof(cmd), "LED 2 0-2 %d %d %d\n", + rv.is_on() ? static_cast(rv.get_red() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_green() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_blue() * rv.get_brightness() * 255.0f + 0.5f) : 0); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: binary + id: gauge2_red_indicator + name: "${gauge2_name} Red Indicator" + output: gauge2_red_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 2 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge2_red_indicator).remote_values.is_on()) { + { const char* cmd = "LED 2 3 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge2_red_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 2 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 2 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 2 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 2 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 2 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 2 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 2 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge2_green_indicator + name: "${gauge2_name} Green Indicator" + output: gauge2_green_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 2 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge2_green_indicator).remote_values.is_on()) { + { const char* cmd = "LED 2 4 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge2_green_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 2 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 2 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 2 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 2 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 2 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 2 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 2 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge2_status_red + name: "${gauge2_name} Status Red" + output: gauge2_status_red_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 2 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge2_status_red).remote_values.is_on()) { + { const char* cmd = "LED 2 5 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge2_status_red).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 2 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 2 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 2 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 2 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 2 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 2 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 2 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge2_status_green + name: "${gauge2_name} Status Green" + output: gauge2_status_green_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 2 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 2 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 2 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge2_status_green).remote_values.is_on()) { + { const char* cmd = "LED 2 6 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge2_status_green).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 2 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 2 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 2 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 2 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 2 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 2 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 2 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: rgb + id: gauge3_backlight + name: "${gauge3_name} Backlight" + red: gauge3_backlight_red_output + green: gauge3_backlight_green_output + blue: gauge3_backlight_blue_output + default_transition_length: 0s + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 3 0-2 800 800 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 3 0-2 150 150 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + char cmd[36]; + snprintf(cmd, sizeof(cmd), "BLINK 3 0-2 100 400 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 3 0-2 3000 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + char cmd[34]; + snprintf(cmd, sizeof(cmd), "BREATHE 3 0-2 1200 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + char cmd[32]; + snprintf(cmd, sizeof(cmd), "DFLASH 3 0-2 %d %d %d\n", + id(gauge3_bl_r), id(gauge3_bl_g), id(gauge3_bl_b)); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + on_state: + - lambda: |- + auto effect = id(gauge3_backlight).get_effect_name(); + if (effect != "None" && !effect.empty()) return; + auto& rv = id(gauge3_backlight).remote_values; + char cmd[32]; + snprintf(cmd, sizeof(cmd), "LED 3 0-2 %d %d %d\n", + rv.is_on() ? static_cast(rv.get_red() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_green() * rv.get_brightness() * 255.0f + 0.5f) : 0, + rv.is_on() ? static_cast(rv.get_blue() * rv.get_brightness() * 255.0f + 0.5f) : 0); + ESP_LOGI("uart_tx", "TX: %s", cmd); + id(arduino_uart).write_str(cmd); + + - platform: binary + id: gauge3_red_indicator + name: "${gauge3_name} Red Indicator" + output: gauge3_red_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 3 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge3_red_indicator).remote_values.is_on()) { + { const char* cmd = "LED 3 3 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge3_red_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 3 3 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 3 3 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 3 3 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 3 3 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 3 3 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 3 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 3 3 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge3_green_indicator + name: "${gauge3_name} Green Indicator" + output: gauge3_green_indicator_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 3 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge3_green_indicator).remote_values.is_on()) { + { const char* cmd = "LED 3 4 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge3_green_indicator).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 3 4 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 3 4 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 3 4 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 3 4 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 3 4 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 3 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 3 4 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge3_status_red + name: "${gauge3_name} Status Red" + output: gauge3_status_red_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 3 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge3_status_red).remote_values.is_on()) { + { const char* cmd = "LED 3 5 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge3_status_red).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 3 5 800 800 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 3 5 150 150 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 3 5 100 400 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 3 5 3000 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 3 5 1200 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 3 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 3 5 255 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } + + - platform: binary + id: gauge3_status_green + name: "${gauge3_name} Status Green" + output: gauge3_status_green_output + restore_mode: ALWAYS_OFF + effects: + - lambda: + name: Blink Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Blink Alert + update_interval: 3600s + lambda: |- + { const char* cmd = "BLINK 3 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Slow + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Breathe Fast + update_interval: 3600s + lambda: |- + { const char* cmd = "BREATHE 3 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + - lambda: + name: Double Flash + update_interval: 3600s + lambda: |- + { const char* cmd = "DFLASH 3 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + on_state: + - lambda: |- + if (!id(gauge3_status_green).remote_values.is_on()) { + { const char* cmd = "LED 3 6 0 0 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + return; + } + auto effect = id(gauge3_status_green).get_effect_name(); + if (effect == "Blink Slow") { + { const char* cmd = "BLINK 3 6 800 800 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Fast") { + { const char* cmd = "BLINK 3 6 150 150 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Blink Alert") { + { const char* cmd = "BLINK 3 6 100 400 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Slow") { + { const char* cmd = "BREATHE 3 6 3000 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Breathe Fast") { + { const char* cmd = "BREATHE 3 6 1200 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else if (effect == "Double Flash") { + { const char* cmd = "DFLASH 3 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } else { + { const char* cmd = "LED 3 6 0 255 0\n"; ESP_LOGI("uart_tx", "TX: %s", cmd); id(arduino_uart).write_str(cmd); } + } +