Skip to content

Instantly share code, notes, and snippets.

@joshtwist
Last active August 7, 2025 09:08
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 flashInterval = null;
let movedSince = null;
let flashTimerTimeout = null;
let accelHandler = null;
// === Configuration Constants ===
const ANGLE_THRESHOLD = 25; // degrees
const SEAT_UP_WARNING_DELAY_MS = 15000;
const ALERT_PHASE_1_END = 60;
const ALERT_PHASE_2_END = 3600;
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);
let state = false;
flashInterval = setInterval(() => {
state = !state;
led.write(state);
}, 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), 100);
}
function startFlashingTimer() {
if (flashTimerTimeout) return;
flashTimerTimeout = setTimeout(() => {
const flashLogic = () => {
const elapsed = (Date.now() - movedSince) / 1000;
if (elapsed < ALERT_PHASE_1_END) {
flashLED(LED1, 100, 400);
} else if (elapsed < ALERT_PHASE_2_END) {
flashLED(LED1, 100, 900);
} else {
flashLED(LED1, 100, 1900);
}
};
flashLogic();
flashInterval = setInterval(flashLogic, 10000);
}, 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), 100);
flashes++;
if (flashes === 3) clearInterval(flash);
}, 300);
accelHandler = (data) => {
if (!data || !seatDownAccel) return;
const angle = angleBetween(data, seatDownAccel);
if (angle > ANGLE_THRESHOLD) {
if (!movedSince) {
movedSince = Date.now();
console.log("Seat is UP - movement detected (angle: " + angle.toFixed(1) + ")");
bluePulse();
startFlashingTimer();
}
} else {
if (movedSince) {
console.log("Seat is back DOWN - resetting timer");
movedSince = null;
stopFlashing();
if (flashTimerTimeout) clearTimeout(flashTimerTimeout);
flashTimerTimeout = null;
}
}
};
Puck.on('accel', accelHandler); // Just start the handler; sensor wakes automatically
}
function stopMonitoring() {
console.log("Monitoring stopped");
monitoring = false;
if (flashInterval) clearInterval(flashInterval);
if (flashTimerTimeout) clearTimeout(flashTimerTimeout);
flashInterval = null;
flashTimerTimeout = null;
LED1.write(0); LED2.write(0); LED3.write(0);
seatDownAccel = null;
movedSince = null;
Puck.removeListener('accel', accelHandler); // stop receiving accel events
accelHandler = null;
}
function ledDancePowerOn(callback) {
let rounds = 3;
let delay = 300;
function runRound(round) {
if (round > rounds) return callback && callback();
LED3.write(1);
setTimeout(() => {
LED3.write(0);
delay *= 0.7;
setTimeout(() => runRound(round + 1), delay);
}, delay / 2);
}
runRound(1);
}
function ledDancePowerOff(callback) {
let rounds = 3;
let delay = 100;
function runRound(round) {
if (round > rounds) return callback && callback();
LED1.write(1); LED2.write(1);
setTimeout(() => {
LED1.write(0); LED2.write(0);
delay *= 1.5;
setTimeout(() => runRound(round + 1), delay);
}, delay / 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