ChipChop Support Forum
Log in
Log out
Join the forum
My Details
REPLIES: 8
VIEWS: 239
BACK
REPLY TO THIS POST
Gizmo
05 Feb 2024
IoT Notifications < post 2 >
This is the continuation of https://forum.chipchop.io/post/310/ on the IoT Notification feature

New Improvement

This is a small improvement to the Notifications system. You have now the option to automatically append to the notification message the status of the component you are monitoring.

This almost works as if you were triggering the notification directly from a device and you can enable/disable it anytime.

You will find a tick box under the notification message field labeled "Include Component Status". When ticked (default) the status of the component will be added after the message

You can see one example in the picture below. The other option where this becomes really interesting is that you can create an extra "fake" component and use it to create whatever status you want like sending a completely custom message from the device each time.

Small downside
This particular setup would require you to sacrifice one component to act as a "notifier" so if you need all 10 components it may be a problem but I will see if I can automatically allocate the "notification" component as a built in part of every device.
The plan is anyway to eventually add a notificationEvent() but that will take time.

Steps:

1. In the Dev Console add a fake "Text" component to your device and name it something like "notifier"
2. On your device based on whatever logic you have you can simply set the status of the "notifier" component and pass it to ChipChop like it was any other real component.




        ...you've done some logic here and you want to notify yourself immediately with a custom message
        
        ChipChop.triggerEvent("notifier","sensors 1 & 2 failure detected"); //sends the status immediately

        // use some keyword that will not trigger the notification action (see Action explanation below)

        ChipChop.updateStatus("notifier", "STOP"); //<< this is important to prevent a flood of messages




3. Create a new action and set

    Trigger Device = Motion Sensors
    Trigger Component = Notifier
    Value is Not Equal != "some short stop value" <<<< use whatever you know you will never send like "stop", "STOP", "pause", "###" etc. This is the keyword we have used in the example above

    Target = Notification
    Notification Text = "Motion Sensors Warning -> " //<< I've added "-> " on purpose at the end with an extra space
    Include Component Status = Ticked


That's it!

The notification that you will receive would look like this Motion Sensors Warning -> sensors 1 & 2 failure detected


So, what is happening here. We are creating an action rule that will execute and trigger the notification on any message (status) except when it encounters the stop keyword. (double negative I guess)
The code on the device is using a triggerEvent to instantly send the message and we are immediately changing the status of the "notifier" component to the stop word so on the next heartbeat the action's logic will be invalid and will be ignored


Enjoy!







Attached images
Hossrod
06 Feb 2024

Woot, thanks!

I got this working, however the notifications wont stop even though I'm using removeStatus().


if ((millis() - loopCycle) > 10000) {

if (WiFi.status() == WL_CONNECTED) {
// Update all component status
ChipChop.updateStatus("BlueLED", blue_led_status);
ChipChop.updateStatus("WhiteLED", white_led_status);
ChipChop.updateStatus("LightSensor", light_sensor_value);

bool prev_light_state = light_state;

if (light_sensor_value > 1600) {
light_state = true;
}
else {
light_state = false;
}

if (prev_light_state != light_state) {
if (light_state) {
Serial.println("Notification sent, light is on.");
ChipChop.triggerEvent("Notification","Office light turned on.");
ChipChop.removeStatus("Notification");
}
else {
Serial.println("Notification sent, light is off.");
ChipChop.triggerEvent("Notification","Office light turned off.");
ChipChop.removeStatus("Notification");
}
}
}
}


I only send notification event in the case that my office light has changed states. I've verified this logic as I only see the serial.print "Notification sent" log message in the monitor when the light changes state and not with each heartbeat.

Am I using it correctly?
Gizmo
06 Feb 2024

sonoffab*** gotya, my bad, wrong instructions!

I did start writing one thing and then brain switched to removeStatus() which is not useful in this situation.

Ok, the correct rule should be to use in the IF statement a keyword that will act as a stop, well, you can actually use the word "stop" or "STOP" or "pause", anything that you think you will never send as a notification and then you use that as a status to stop the flow:

So, if:

IF value IS not "STOP" ....send notification



    ChipChop.triggerEvent("Notification","Office light turned on.");
    ChipChop.updateStatus("Notification","STOP");



Try that, it should work correctly and should make more sense to simply say "Notification":"stop" rather than abstracting with removeStatus()

Been bombarded by phone calls today, really killed my concentration and I've done all this in a brief pause between testing the Keep Alive (so far looking good, just had one small aneurism popping)

It is working as intended just wrote completely wrong instructions. If I'm wrong this time then I'm re-qualifying and going to be a turnip farmer.

This is probably the best illustration of what the event flow looks inside ChipChop :-) Right I better re-write that explanation and then finish for today.




Attached images
Hossrod
06 Feb 2024

lol, that photo looks like my thought processes on a good day.

I got it working!

Note, I did have to put a delay between triggerEvent() and the updateStatus() otherwise I got an error in the serial monitor that I'm sending requests too fast. I didn't see that the error message said 500ms and I first tried 100ms which did seem to work. But to be safe, I have it at 500ms now.


if (prev_light_state != light_state) {
if (light_state) {
Serial.println("Notification sent, light is on.");
ChipChop.triggerEvent("Notification","Office light turned on.");
}
else {
Serial.println("Notification sent, light is off.");
ChipChop.triggerEvent("Notification","Office light turned off.");
}

delay(500);
ChipChop.updateStatus("Notification", "Stop");
}


Thanks Gizmo!
Gizmo
06 Feb 2024

Yes, it's possible that it's a tad too quick, simply didn't have the chance to properly play with it.
The max speed for any messages to ChipChop is 500ms but the library should have caught that. Can you just check please if it was exactly like this Event will not be sent. Max requests speed of 500 milliseconds between events has been exceeded. or was it starting width {"status":"error <some code and warning> ....

If it's the first than it's just the library internally preventing a system violation on the triggerEvent, if it's the second than it's ChipChop itself giving a slap on the wrist :-)
I'll reproduce it here and see if I need to do some tweaking...oh..just had a taught, I wander if I could make a "try/catch" on the triggerEvent so you know if it fails and you can resend it few millis later?
From memory there is a check for that that prevents heartbeat/triggerEvent collision and it will delay the triggerEvent if it's too close to the heartbeat, from memory it extends a delay to 501ms but that can depend on the processor so if your 100ms worked than it's possibly just me extending that collision delay by 10-20 ms more.

Anyway, the way I would probably do it is something like this below. And you can lose the entire WiFi & 10sec check, especially for the triggerEvent because you don't want to delay that. I guess you can encapsulate the updateStatus in a 10 sec check but the C6 has soo much juice that it won't break any sweat if you don't and just leave the timing to the library.

Try this, I'm interested if it would work ok or still needs a delay()



void loop(){

    bool prev_light_state = light_state;

    if(light_sensor_value > 1600){
    light_state = true;
    }else {
        light_state = false;
    }

    if (prev_light_state != light_state) {
        //here we are just doing a temporary change of the status and it will reset itself
        // in the updateStatus part below
        if(light_state){
            ChipChop.triggerEvent("Notification","Office light turned on.");
        }else {

            ChipChop.triggerEvent("Notification","Office light turned off.");
        }
    }


    // Do all the checks first and update all component status at the end
    ChipChop.updateStatus("BlueLED", blue_led_status);
    ChipChop.updateStatus("WhiteLED", white_led_status);
    ChipChop.updateStatus("LightSensor", light_sensor_value);

    ChipChop.updateStatus("Notification", "STOP");


}


Hossrod
07 Feb 2024

I tried your example with no delay and got these errors...


23:23:40.176 -> {"api_call":"heartbeat","command":"heartbeat","uuid":"xxxx","device_id":"000001","status":{"Notification":{"value":"Stop"},"BlueLED":{"value":"OFF"},"WhiteLED":{"value":"OFF"},"LightSensor":{"value":2288},"Temperature":{"value":70.36}}}
23:23:41.471 -> {"status":"ok","timestamp":1707261820373}
23:23:50.834 -> {"api_call":"heartbeat","command":"triggerevent","uuid":"xxxx","device_id":"000001","status":{"Notification":{"value":"Office light turned off."}}}
23:23:50.866 -> {"api_call":"heartbeat","command":"heartbeat","uuid":"xxxx","device_id":"000001","status":{"Notification":{"value":"Stop"},"BlueLED":{"value":"OFF"},"WhiteLED":{"value":"OFF"},"LightSensor":{"value":16},"Temperature":{"value":70.36}}}
23:23:52.192 -> {"status":"ok","timestamp":1707261831383}
23:23:52.192 -> {"error":"error 408: message rejected, maximum query speed exceeded. Reduce message frequency to avoid system violation"}
23:23:52.192 -> error 408: message rejected, maximum query speed exceeded. Reduce message frequency to avoid system violation


This is with the 10 second delay loop still intact (but did remove the WiFi connection check).


if ((millis() - loopCycle) > 10000) {

bool prev_light_state = light_state;

if (light_sensor_value > 1600) {
light_state = true;
}
else {
light_state = false;
}

if (prev_light_state != light_state) {
if (light_state) {
ChipChop.triggerEvent("Notification","Office light turned on.");
}
else {
ChipChop.triggerEvent("Notification","Office light turned off.");
}

// Update all component status
ChipChop.updateStatus("BlueLED", blue_led_status);
ChipChop.updateStatus("WhiteLED", white_led_status);
ChipChop.updateStatus("LightSensor", light_sensor_value);
ChipChop.updateStatus("Temperature", temperature_sensor.getTempFByIndex(0));
ChipChop.updateStatus("Notification", "Stop");
}

loopCycle = millis(); // reset the variable to the current time
}
Hossrod
07 Feb 2024

Hmm, I found it only repros if I have another timed (displayLoopCycle) loop set to 500. When its at 200, I can't get ChipChop to throw that error.

I dont know a lot about timing in ESP32, but guessing the two timed loops are messing with each other somehow??


void loop()
{
ChipChop.run(); // This call has to be here in the main loop() and needs to run continously

light_sensor_value = analogRead(LIGHT_SENSOR_PIN);
temperature_sensor.requestTemperatures();

if ((millis() - displayLoopCycle) > 200) {
display_line1 = "Light Sensor = " + String(light_sensor_value);
display_line3 = "BlueLED = " + blue_led_status;
display_line4 = "WhiteLED = " + white_led_status;
display_line5 = "Temperature = " + String((int)temperature_sensor.getTempFByIndex(0)) + " degrees";
displayUpdate();

displayLoopCycle = millis();
}

<The ChipCode in previous post>

loopCycle = millis(); // reset the variable to the current time
}
}
Gizmo
07 Feb 2024

right, can you pls email me the whole code (the version that will produce the warning) and I'll run it on an esp here, I have stuff scanning sensors at multiple 5ms timers and firing warnings when needed with no problems so it would be good to find out what combo of circumstances would issue a warning.

Looks like you are using a display as well, but that shouldn't affect anything unless it has something dodgy in it's own library !?

I had a look at what I've implemented in the library as a clash collision avoidance for triggerEvents and there is one line that I've put there on purpose to try to give best chance at sending the event and haven't had a problem with my stuff so far but I am not dismissing that check as being maybe too "trigger happy" <<< pun intended :-))))

I write the code kinda instinctively knowing what would or wouldn't work so I'm not the best benchmark :-)

That's why I beg people to post any issue straight away, it's impossible to predict every possible use case and anything I can reproduce I can find a solution for.


p.s. Hey, thanks for being a test pilot, I'll get you a decent crash helmet for Christmas...lol
Gizmo
08 Feb 2024

Ok, I've done a battery of tests and to make life easier I'll start a new post https://forum.chipchop.io/post/364/