Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
12.08k stars 1.68k forks source link

Moving from zigbee-HA to zigbee2mqtt - Missing "no_occupancy_since" State for Device RTCGQ11LM #23572

Open HannesMC opened 2 months ago

HannesMC commented 2 months ago

What happened?

I'm moving from Zigbee-HA to Zigbee2MQTT. In Zigbee-HA, my Motion-Sensor Aqara RTCGQ11LM has a "no_motion" state. I think this state is similar to "no_occupancy_since" in Zigbee2MQTT. Unfortunately the state "no_occupancy_since" missing in iobroker.

What did you expect to happen?

I expected the state "no_occupancy_since" to appear in iobroker.

How to reproduce it (minimal and precise)

Pairing a RTCGQ11LM and check if state "no_occupancy_since" is not missing.

Zigbee2MQTT version

1.39.1 commit: e132316

Adapter firmware version

20230507

Adapter

Slaesh's CC2652RB

Setup

Z2M together with eclipse-mqtt as docker-stack in portainer. docker-host = debian (VM in Proxmox)

Debug log

2024-08-09 11:07:29z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0x00158d0007752803', payload '{"battery":100,"device_temperature":32,"illuminance":20,"illuminance_lux":20,"linkquality":72,"no_occupancy_since":600,"occupancy":false,"power_outage_count":123,"voltage":3005}'

If i configure the my specific values, it doesn´t work. I changed it to: image

HannesMC commented 2 months ago

In the meantime i have figured out a workaround, to solve the missing "no_occupancy_since" Problem.

It's just a small Javascript, which monitors the state "occupancy" and starts a Counter and writes the seconds in an separate State.

As long as the "Occupancy" state does not change, it counts up to 1200 seconds. After that, no more updates are made. If the "Occupancy" state changes during or after the count-up, the count-up is restarted. Initial counting takes place when Occupancy is true. If Occupancy is false, the set maximum value of seconds is written to the state (default 1200 seconds).

// Bewegungsmelder-Datenpunkte
const motionSensor1 = "zigbee2mqtt.0.0x00158d0007752803.occupancy";  // Ersetze dies durch den tatsächlichen Pfad zum Bewegungsmelder 1
const motionSensor2 = "zigbee2mqtt.0.0x00158d000775a2dd.occupancy";  // Ersetze dies durch den tatsächlichen Pfad zum Bewegungsmelder 2

// Ziel-Datenpunkte für die Sekunden seit letzter Bewegung
const state1 = "0_userdata.0.Custom_Variables.Treppenaufgang.Bewegungsmelder_EG.no_occupancy_since";
const state2 = "0_userdata.0.Custom_Variables.Treppenaufgang.Bewegungsmelder_OG.no_occupancy_since";

// Maximale Sekundenanzahl (1200 Sekunden = 20 Minuten)
const maxSeconds = 1200;

// Variable für die Zeitintervalle in Sekunden, nach denen der Zähler erhöht wird
const interval = 10;  // Setze diesen Wert auf die gewünschte Anzahl an Sekunden

// Timer-Variablen
let timer1 = null;
let timer2 = null;

// Funktion zum Starten oder Zurücksetzen des Timers für einen Sensor
function startOrResetTimer(sensor, state, timer) {
    if (timer) clearInterval(timer);  // Vorherigen Timer stoppen

    setState(state, 0);  // Zähler auf 0 setzen

    // Neuen Timer starten
    return setInterval(() => {
        let currentValue = getState(state).val;
        if (currentValue < maxSeconds) {
            setState(state, currentValue + interval);
        } else {
            clearInterval(timer);  // Timer stoppen, wenn maxSeconds erreicht sind
        }
    }, interval * 1000);  // Intervall in Millisekunden (z.B. alle 10 Sekunden)
}

// Bewegungsmelder 1 überwachen
on({id: motionSensor1, change: "ne"}, function (obj) {
    if (obj.state.val && !obj.oldState.val) {  // Wechsel von false auf true
        timer1 = startOrResetTimer(motionSensor1, state1, timer1);
    }
});

// Bewegungsmelder 2 überwachen
on({id: motionSensor2, change: "ne"}, function (obj) {
    if (obj.state.val && !obj.oldState.val) {  // Wechsel von false auf true
        timer2 = startOrResetTimer(motionSensor2, state2, timer2);
    }
});

// Initialisierung: Timer oder Initialwert setzen
function initializeState(sensor, state) {
    if (getState(sensor).val) {  // Wenn Occupancy auf true steht
        timer1 = startOrResetTimer(sensor, state, timer1);  // Timer starten
    } else {  // Wenn Occupancy auf false steht
        setState(state, maxSeconds);  // Wert auf 1200 Sekunden setzen
    }
}

// Initialisierung für beide Bewegungsmelder
initializeState(motionSensor1, state1);
initializeState(motionSensor2, state2);