Updating the rule fixed the issue, I was still on an older version. Is there btw a convenient way to update existing rules with new versions through the addon store instead of recreating the rules everytime there’s an update?
HI @rlkoshak,
finally had time to pull debug logs for my Undef bug.
It seems like there’s an edge case leading to a comparison of UNDEF != UnDefType
if the item state is an empty string.
In my case, before the item state changes from “Storm Warning” to “UNDEF”, it changes to an empty string for whatever reason, which masked the problem originally.
- item.state == “Storm Warning”
- item.state == “”
- item.state == “UNDEF”
I see in the debug logs that the rule treats "UNDEF"
correctly and converts it to UnDefType
. The empty string, however, is ignored. Comparing an empty string to the threshold leads to a conversion of UNDEF != UnDefType
as shown in the logs below.
2024-11-22 12:18:55.164 [DEBUG] [LocalWeather_Warnings_1_Alerts_Event] - state is the empty string, no conversion possible
2024-11-22 12:18:55.165 [DEBUG] [LocalWeather_Warnings_1_Alerts_Event] - Checking if we are in the alerting state: UNDEF != UnDefType
2024-11-22 12:18:55.165 [DEBUG] [LocalWeather_Warnings_1_Alerts_Event] - Result is true
My first thought to fix this would be converting empty strings to UnDefType as well? What do you think @rlkoshak?
Unfortunately not. You gotta remove the old template, add it again to get the new, then recreate your rules using the new template.
Alternatively you can copy everything after the //-----Functions
comment (going from memory, it may not look exactly like that) from the template on github and paste it over the the contents in the Code tab. That will preserve your config. Just make sure to update the version number at that top of the Script Action so you know which version you are running with.
I am not sure about that approach. That means you’d never be able to use a threshold that is the empty string distinct from UnDefType. I’ll have to think about it some. It might be as simple as moving around the order of the conversions in the stateToValue function.
Thanks for reporting it. I’ll hgave a look into it over the weekend.
I have a Shelly Blu Motion. When motion is detected, it switches to ON. It switches back to OFF, after the set “blind time” (min 30s, max 600s), unless a motion is detected during the blind time. In the latter case it stays ON.
How could configure this rule template such that a light a switched ON when a motion is detected, and stays ON for 2 minutes after the motion detector has gone to OFF.
Thanks for your help.
It might be easier to just use a simple timer for this use case if you only have the one sensor and light. But the rule template can do this.
- put all the motion sensors into a Group, I’ll call it MotionSensors.
- Create a rule or script that commands the light corresponding with the motion sensor to on or off. Identifying which light goes with which sensor as an exercise left to you. The semantic model can be handy for this. The name of the motion sensor will be
alertItem
. The way we will configure the rule your rule will turn on the light whenisAlerting
istrue
and turn off the light whenisAlerting
isfalse
. - Configure the rule template as follows:
- Triggering Group: the group created in 1
- Threshold State: OFF
- Comparison Operator: =
- Alert Delay: PT2M
- Alert Rule: select the rule created in 2
- End Alert Rule: select the rule created in 2
- Set the DND start and end for the time period you do not want the sensor to control the light.
Leave everything else at the default.
your rule will get called when the sensor changes to ON and two minutes after it changes to OFF except during the DND time period.
Thanks I will give it a try .
I am trying to implement the motion sensor control through this ruletemplate, as in the example in the second post. But I’m stumbling on two issues that I don’t understand / don’t know how to work around.
My template properties are:
var group = 'g_LightTrigger_PresenceItems__overiden';
var thresholdStr = 'ON';
var operator = '==';
var comparison = (currState, threshold) => { return currState == threshold; };
var invert = false;
var defaultAlertDelay = 'PT10M'; // 10 Minutes
var defaultRemPeriod = '';
var namespace = 'MotionLights';
var alertRuleUID = 'threshold_initialalert_motionlights_on';
var endAlertUID = '';
var dndStart = '00:00';
var dndEnd = '00:00';
var gkDelay = 0;
var hystRange = '';
var rateLimitPeriod = '';
var reschedule = true;
var initAlertRuleUID = 'threshold_initialalert_motionlights_on';
The ‘threshold…on’ rule is the same as in the example.
My desktop computer updates a ‘motionsensor’ every two minutes if I am typing. Thus, If I start typing, the sensor goes from OFF to ON and the lights turn on (with initiAlert). As long as I am typing, the sensor keeps getting updated and the timer is rescheduled again. After 10 minutes of not typing the lights are turned off by the ‘Alert’ rule and the typingsensor is turned off.
Now I have the situation that I start typing and the lights go on. After one minute I walk out of the room and turn off the lights with the wallswitch. I return two minutes later and start typing again. The lights wont turn on, the alert is rescheduled and I have to turn them om manually.
I think my main question is, if there is a way for the ruletemplate to know I have turned off the lights manually and stop the alert/alert timer?
Hmmmm, this is a tricky one. The problem here is the Item we care about (the light) is different from the triggering Item (the motion sensor). I don’t think there is any way to get this to happen from the template rule.
However, if you create another rule that triggers when the light changes to OFF and in that rule update the motion sensor to OFF immediately that should trigger the threshold alert rule which will see that the motion sensor is no longer in the alerting state and it will exit alerting. Then when you start typing again, even if it’s right away, it reenters the alerting state and will immediately call the initAlertRule.
This should work well because it’s not a real motion sensor so it shouldn’t pick up any motion after you’ve turned off the light. Even if it were a real motion sensor, you could probably use a timer to delay updating the motion sensor Item to OFF to avoid those stray detections as you exit the room. But since you use typing on the keyboard as the sensor, there’s no problem immediately updating the Item.
Have you figured a way how to eventually deal with those empty string checks?
I started to and then forgot about it, so thanks for the ping as a reminder.
I’m not actually seeing where/how this is happening and that’s part of the problem.
The state of the Item is always converted to a string for comparison with a call to stateToValue
. If the state
is the empty string, the empty string is what’s returned.
else if(state == '') {
console.debug('state is the empty string, no conversion possible');
return state;
}
So where the heck is UNDEF
coming from? That log statement should read: Checking if we are in the alerting state: != UnDefType
because the currState
should still be the empty string.
Once I figure that out I can figure out how to address the issue. But it’s a bit of a mystery to me. Something really weird is going on, and it doesn’t make sense and so far I’ve not been able to duplicate the behavior.
Working backwards, the “Checking if we are in the alerting state” log only occurs in the isAlertingState()
. Every time isAlertingState
is called, the current state of the Item is converted to a string through a call to stateToValue()
which is returning the empty string if the state is the empty string. And there isn’t a lot between the calls to stateToValue()
and the call it isAlertingState()
where something could go wrong.
I can’t figure out where “UNDEF” could possibly be coming from.
I might have, but I do not understand
In your function sendAlertGenerator = (record) =>
you are using rawState
for the state to value conversion. With rawState, state to value for whatever reason returns ‘UNDEF’ as a string. If I change rawState
and use state
instead, UnDefType is returned as expected.
I think there might be a problem with the type of rawState
which makes the rule fail. I am not sure if this is related, but I also see the following log line Not numeric, leaving as a string: UNDEF
But you can easily reproduce this behavior yourself:
var thresholdStr = 'UnDefType';
var operator = '!=';
Then changle the string to “Anything” to trigger the rule, then set it to “UNDEF” right after. For “UNDEF”, the rule should not trigger, but it does unless you change rawState to state.
I tested with this:
item.postUpdate('Storm Warning');
setTimeout(function() {
item.postUpdate('UNDEF');
}, 5000);
With the rawState
I get two alerts. With state
I only get the first one as expected. The thing with the empty string was unrelated it turns out.
In the currently posted version of the template I cannot find rawState
anywhere in the code. And I don’t pass the record to sendAlertGenerator
, I pass the name of the Item and pull the record.
/**
* Creates the function that gets called by the loopingTimer for a given Item. The generated
* function returns how long to wait for the next call (adjusted for DND) or null when it's
* time to exit.
*
* @param {String} name of the Item this alert is for
* @return {function} function that takes no arguments called by the looping timer
*/
var sendAlertGenerator = (name) => {
return () => {
console.loggerName = loggerBase+"."+name;
const record = cache.private.get('records')[name];
const item = items[name];
const currState = stateToValue(items[name].state);
refreshRecord(record);
console.debug('Alert timer expired for ' + record.name + ' with dnd between ' + record.dndStart + ' and ' + record.dndEnd + ' and reminder period ' + record.remPeriod);
let repeatTime = generateAlertTime(record.remPeriod, record.dndStart, record.dndEnd); // returns null if remPeriod is '', which cancels the loop
// Call alert rule if still alerting
if(isAlertingState(currState, record)) {
console.debug(name + ' is still in an alerting state.');
callRule(currState, record.alertRule, true, false, record);
if(!record.alerted) record.alertState = currState;
record.alerted = true;
if(repeatTime === null) record.alertTimer = null; // clean up if no longer looping
console.debug('Waiting until ' + repeatTime + ' to send reminder for ' + name);
return repeatTime;
}
// no longer alerting, cancel the repeat, send an alert if configured
else {
console.debug(name + ' is no longer in an alerting state but timer was not cancelled, this should not happen');
record.alertTimer = null;
return null;
}
}
}
Are you running the latest? It should say v1.4 at the top of the script action.
OK, previously this was
const currState = stateToValue(items[name].rawState);
So the problem is gone in 1.4.
Thx!
I made that change in 1.3 just because it seemed better. Apparently doing it the old way is indeed a bug