refactor: rename example_button_log.js to watterButtonLearnAndCall.js
This commit is contained in:
144
rules/watterButtonLearnAndCall.js
Normal file
144
rules/watterButtonLearnAndCall.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Timer light rule
|
||||
*
|
||||
* - btn_down: Light goes off
|
||||
* - long_push: Flash to confirm, enter "count mode" - counts seconds until next btn_down, then light goes on
|
||||
* - Normal btn_down (no long_push): Light goes off, then turns back on after the stored count elapsed
|
||||
*
|
||||
* Also syncs remote switch CC8DA243B0A0 inversely (light off = switch on, light on = switch off)
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const STATE_FILE = path.join(__dirname, 'timer_state.json');
|
||||
const REMOTE_SWITCH_MAC = 'CC8DA243B0A0';
|
||||
|
||||
// Helper to set local light and sync remote switch inversely
|
||||
async function setLight(ctx, mac, on, brightness) {
|
||||
await ctx.sendRPC(mac, 'Light.Set', { id: 0, on, brightness });
|
||||
// Remote switch is inverse: light off = switch on, light on = switch off
|
||||
await ctx.sendRPC(REMOTE_SWITCH_MAC, 'Switch.Set', { id: 0, on: !on });
|
||||
}
|
||||
|
||||
// Load persisted state
|
||||
function loadPersistedState() {
|
||||
try {
|
||||
if (fs.existsSync(STATE_FILE)) {
|
||||
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading state:', err);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Save state to file
|
||||
function saveState(state) {
|
||||
try {
|
||||
// Only save storedDuration per MAC
|
||||
const toSave = {};
|
||||
for (const [mac, data] of Object.entries(state)) {
|
||||
toSave[mac] = { storedDuration: data.storedDuration };
|
||||
}
|
||||
fs.writeFileSync(STATE_FILE, JSON.stringify(toSave, null, 2));
|
||||
} catch (err) {
|
||||
console.error('Error saving state:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// State for devices (in memory, with persistence for storedDuration)
|
||||
const persistedState = loadPersistedState();
|
||||
const deviceState = new Map();
|
||||
|
||||
function getState(mac) {
|
||||
if (!deviceState.has(mac)) {
|
||||
const persisted = persistedState[mac] || {};
|
||||
deviceState.set(mac, {
|
||||
countMode: false,
|
||||
countStart: 0,
|
||||
storedDuration: persisted.storedDuration || 5000, // Default 5 seconds
|
||||
timer: null
|
||||
});
|
||||
}
|
||||
return deviceState.get(mac);
|
||||
}
|
||||
|
||||
export default {
|
||||
async run(ctx) {
|
||||
if (ctx.trigger.field !== 'button') return;
|
||||
|
||||
const mac = ctx.trigger.mac;
|
||||
const state = getState(mac);
|
||||
|
||||
ctx.log(`Event: ${ctx.trigger.event}, countMode: ${state.countMode}, storedDuration: ${state.storedDuration}ms`);
|
||||
|
||||
// Handle btn_down
|
||||
if (ctx.trigger.event === 'btn_down') {
|
||||
// Clear any pending timer
|
||||
if (state.timer) {
|
||||
clearTimeout(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
|
||||
// Turn light off (remote switch turns on)
|
||||
await setLight(ctx, mac, false, 0);
|
||||
|
||||
if (state.countMode) {
|
||||
// We're in count mode - calculate elapsed time and turn light on
|
||||
const elapsed = Date.now() - state.countStart;
|
||||
state.storedDuration = elapsed;
|
||||
state.countMode = false;
|
||||
ctx.log(`Count mode ended. Stored duration: ${elapsed}ms`);
|
||||
|
||||
// Persist the new duration
|
||||
persistedState[mac] = { storedDuration: elapsed };
|
||||
saveState(persistedState);
|
||||
|
||||
// Turn light on immediately (remote switch turns off)
|
||||
await setLight(ctx, mac, true, 20);
|
||||
} else {
|
||||
// Normal mode - schedule light to turn on after stored duration
|
||||
ctx.log(`Light off. Will turn on in ${state.storedDuration}ms`);
|
||||
|
||||
state.timer = setTimeout(async () => {
|
||||
ctx.log(`Timer elapsed. Turning light on.`);
|
||||
await setLight(ctx, mac, true, 20);
|
||||
state.timer = null;
|
||||
}, state.storedDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle long_push - enter count mode
|
||||
if (ctx.trigger.event === 'long_push' && !state.countMode) {
|
||||
// Clear any pending timer
|
||||
if (state.timer) {
|
||||
clearTimeout(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
|
||||
ctx.log('Entering count mode...');
|
||||
|
||||
// Flash to confirm (don't sync remote during flash)
|
||||
for (let i = 0; i < 2; i++) {
|
||||
await ctx.sendRPC(mac, 'Light.Set', { id: 0, on: true, brightness: 20 });
|
||||
await sleep(200);
|
||||
await ctx.sendRPC(mac, 'Light.Set', { id: 0, on: false, brightness: 0 });
|
||||
await sleep(200);
|
||||
}
|
||||
|
||||
// Ensure light stays off during count mode (remote switch on)
|
||||
await setLight(ctx, mac, false, 0);
|
||||
|
||||
// Enter count mode
|
||||
state.countMode = true;
|
||||
state.countStart = Date.now();
|
||||
ctx.log('Count mode active. Light stays off. Press button to set duration and turn on.');
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user