Created
September 24, 2026 15:04
-
-
Save pliablepixels/3c7c3f6e2f8d2fdb89303651306ec792 to your computer and use it in GitHub Desktop.
Zee Claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 1. The 7pm flood is the rules file I gave him, working as written | |
| My file only muted non-person events between 7:00 am and 6:59 pm. Nothing muted anything after 7 pm. So at 7:00 pm the mute window closed and every event — cars, animals, plain motion — started notifying. I flagged this in passing ("a car at 10pm still sends a normal notification") but I should have just put the night mute in the file. That's my error, not a bug in his setup. | |
| His workaround, one all-day mute on non-person, is the right shape. It just drops the critical escalation. | |
| 2. cause_has: "person" really does match "no Person" | |
| Rules.pm:131-132 lowercases the cause and matches the pattern with /i as a plain substring regex. There's no word boundary and no negation awareness. I tested the patterns against realistic cause strings: | |
| ┌───────────────────────────────────┬─────────┬──────────────────┐ | |
| │ Cause │ person │ ^(?!.*person).*$ │ | |
| ├───────────────────────────────────┼─────────┼──────────────────┤ | |
| │ Back Door no Person │ matches │ no │ | |
| ├───────────────────────────────────┼─────────┼──────────────────┤ | |
| │ [s] detected:person:88% Back Door │ matches │ no │ | |
| ├───────────────────────────────────┼─────────┼──────────────────┤ | |
| │ [s] detected:car:91% Driveway │ no │ matches │ | |
| ├───────────────────────────────────┼─────────┼──────────────────┤ | |
| │ Motion: Person Zone │ matches │ no │ | |
| └───────────────────────────────────┴─────────┴──────────────────┘ | |
| So his observation is correct. Any cause containing the letters "person" anywhere — in a negation like "no Person", in a zone name, in a monitor name — matches cause_has: "person". At night that escalated non-detections to critical, and by day it defeated the mute, because the negative lookahead also sees that "person". | |
| Anchoring the pattern to the ML output fixes it. The hook writes detections as detected:<label> (HookProcessor.pm:461,515), so: | |
| - escalate: cause_has: "detected:.*person" | |
| - mute: cause_has: "^(?!.*detected:.*person).*$" | |
| The same table with those patterns: Back Door no Person is muted and not escalated, detected:person is escalated, detected:car is muted, Motion: Person Zone is muted. That's the intended behavior in all four cases. | |
| Where "no Person" comes from is still unknown. It isn't a string this repo or pyzmNg produces. Likely a ZM zone or monitor name, or something another tool wrote into the event. The logs will show it. | |
| 3. Suggested rules file | |
| outdoor_rules: &outdoor | |
| rules: | |
| - comment: "Mute anything that is not an ML person detection, any time" | |
| time_format: "%I:%M %p" | |
| from: "12:00 am" | |
| to: "11:59 pm" | |
| cause_has: "^(?!.*detected:.*person).*$" | |
| action: mute | |
| - comment: "Evening - person detections are critical" | |
| time_format: "%I:%M %p" | |
| from: "7:00 pm" | |
| to: "11:59 pm" | |
| cause_has: "detected:.*person" | |
| action: critical_notify | |
| - comment: "After midnight - person detections are critical" | |
| time_format: "%I:%M %p" | |
| from: "12:00 am" | |
| to: "6:59 am" | |
| cause_has: "detected:.*person" | |
| action: critical_notify | |
| indoor_rules: &indoor | |
| rules: | |
| - comment: "Indoor - always mute" | |
| time_format: "%I:%M %p" | |
| from: "12:00 am" | |
| to: "11:59 pm" | |
| action: mute | |
| notifications: | |
| monitors: | |
| 1: *outdoor | |
| 2: *outdoor | |
| 3: *outdoor | |
| 4: *outdoor | |
| 5: *outdoor | |
| 6: *indoor | |
| 7: *indoor | |
| The mute comes first and covers the whole day, so non-person events never reach the escalation rules. Daytime person events fall through every rule and notify normally. This also avoids the midnight bug, so it works on his current version. | |
| 4. Getting the logs | |
| The ES logs through ZoneMinder's logger, which is why nothing lands in a file until ZM is told to write one. docs/guides/es_faq.rst:605-640 has this. In ZM → Options → Logs: | |
| - LOG_DEBUG on | |
| - LOG_LEVEL_FILE = Debug | |
| - LOG_DEBUG_TARGET = _zmesdetect|_zmeventnotification | |
| - restart ZM | |
| Then: | |
| tail -F /var/log/zm/zmeventnotification.log /var/log/zm/zmesdetect*.log | |
| What to look for, all from Rules.pm: | |
| - rules: Checking rules for alarm caused by eid:... cause:<text> — the exact cause string the rules see. This is the one that matters, and it will show what "no Person" really is. | |
| - rules: seeing if cause_has: -> ... <- is part of -> ... <- | |
| - rules: Skipping this rule as ... does not pattern match ... | |
| - rules: <action> rule matched or rules: No rules matched | |
| He can also run it in the foreground, which logs to the terminal without touching ZM's options at all: | |
| sudo -u www-data /usr/bin/zmeventnotification.pl --debug | |
| Worth asking him for a few of those Checking rules for alarm lines. They settle what the cause text actually is, which is the one thing this analysis is guessing at. | |
| One more thing to tell him: the midnight fix I merged isn't in a release yet, so he's still on a version where a single 7pm-to-7am window only works after midnight. The file above doesn't rely on it. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment