Skip to content

Instantly share code, notes, and snippets.

@joshtwist
Last active June 20, 2025 14:21
Show Gist options
  • Save joshtwist/7a13ad0d5967a351de303c984770ac71 to your computer and use it in GitHub Desktop.
Save joshtwist/7a13ad0d5967a351de303c984770ac71 to your computer and use it in GitHub Desktop.
puckJS toilet seat code
let monitoring = false;
let seatDownAccel = null;
let checkInterval = null;
let flashInterval = null;
let movedSince = null;
let flashTimerInterval = null;
// === Configuration Constants ===
const ANGLE_THRESHOLD = 25; // degrees
const SEAT_UP_WARNING_DELAY_MS = 15000; // delay before red LED starts flashing
const ALERT_PHASE_1_END = 60; // 15s–1min: fast flashing
const ALERT_PHASE_2_END = 3600; // 1min–1hr: medium flashing
function angleBetween(v1, v2) {
const dot = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
const mag1 = Math.sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);
const mag2 = Math.sqrt(v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);
const cosTheta = dot / (mag1 * mag2);
return Math.acos(Math.min(Math.max(cosTheta, -1), 1)) * (180 / Math.PI);
}
function flashLED(led, onTime, offTime) {
if (flashInterval) clearInterval(flashInterval);
LED1.write(0); LED2.write(0); LED3.write(0);
flashInterval = setInterval(() => {
led.write(1);
setTimeout(() => led.write(0), onTime);
}, onTime + offTime);
}
function stopFlashing() {
if (flashInterval) clearInterval(flashInterval);
flashInterval = null;
LED1.write(0); LED2.write(0); LED3.write(0);
}
function bluePulse() {
LED3.write(1);
setTimeout(() => LED3.write(0), 1000);
}
function startFlashingTimer() {
if (flashTimerInterval) return;
flashTimerInterval = setTimeout(() => {
flashInterval = setInterval(() => {
const elapsed = (Date.now() - movedSince) / 1000;
if (elapsed < ALERT_PHASE_1_END) {
flashLED(LED1, 100, 233); // fast
} else if (elapsed < ALERT_PHASE_2_END) {
flashLED(LED1, 500, 500); // medium
} else {
flashLED(LED1, 100, 900); // slow
}
}, 1000);
}, SEAT_UP_WARNING_DELAY_MS);
}
function startMonitoring() {
seatDownAccel = Puck.accel().acc;
console.log("Calibrated: seat is CLOSED");
movedSince = null;
// Blink green 3x
let flashes = 0;
let flash = setInterval(() => {
LED2.write(1);
setTimeout(() => LED2.write(0), 200);
flashes++;
if (flashes === 3) clearInterval(flash);
}, 400);
checkInterval = setInterval(() => {
const current = Puck.accel().acc;
if (!current || !seatDownAccel) return;
const angle = angleBetween(current, seatDownAccel);
if (angle > ANGLE_THRESHOLD) {
if (!movedSince) {
movedSince = Date.now();
console.log("Seat is UP - movement detected");
bluePulse();
startFlashingTimer();
}
} else {
if (movedSince) {
console.log("Seat is back DOWN - resetting timer");
movedSince = null;
stopFlashing();
if (flashTimerInterval) clearTimeout(flashTimerInterval);
flashTimerInterval = null;
}
}
}, 1000);
}
function stopMonitoring() {
console.log("Monitoring stopped");
monitoring = false;
if (checkInterval) clearInterval(checkInterval);
if (flashInterval) clearInterval(flashInterval);
if (flashTimerInterval) clearTimeout(flashTimerInterval);
flashInterval = null;
flashTimerInterval = null;
LED1.write(0); LED2.write(0); LED3.write(0);
seatDownAccel = null;
movedSince = null;
checkInterval = null;
}
function ledDancePowerOn(callback) {
let rounds = 4;
let baseDelay = 400;
function runRound(round) {
if (round > rounds) return callback && callback();
LED3.write(1);
setTimeout(() => {
LED3.write(0);
baseDelay *= 0.6; // speed up
setTimeout(() => runRound(round + 1), baseDelay);
}, baseDelay / 2);
}
runRound(1);
}
function ledDancePowerOff(callback) {
let rounds = 4;
let baseDelay = 100;
function runRound(round) {
if (round > rounds) return callback && callback();
LED1.write(1); LED2.write(1); // yellow
setTimeout(() => {
LED1.write(0); LED2.write(0);
baseDelay *= 1.5; // slow down
setTimeout(() => runRound(round + 1), baseDelay);
}, baseDelay / 2);
}
runRound(1);
}
// Button toggle
setWatch(() => {
if (!monitoring) {
monitoring = true;
ledDancePowerOn(startMonitoring);
} else {
ledDancePowerOff(stopMonitoring);
}
}, BTN, { edge: "rising", debounce: 50, repeat: true });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment