HowTo: Get notified on empty batteries

On 3.0 ? what post you use for this ?
I tried several but each time I have errors, javascript even bug for the “Hello World”.

thanks in advance

What do you mean by what post?

To install jython I used this procedure, python script works, on the other hand javascript, bug all the time, I tried several times the procedure but without success, if you succeeded maybe you have a link describing it, I must obviously missing something :slight_smile:
I would like to order the javascript files on my test server 3.0.

thanks in advance

I didn’t install anything else. Mine is just a plain install of OH3.

I have these bindings: MQTT,mail,Astro.
I have no DSL rules and only ECMAScript (JavaScript).
My set up is very simple.

Start a new post for this, instead of adding on to a tutorial about something else,it’ll work out better.

Thank you rossko57, this is what will do it will indeed be clearer :+1:

hi @ubeaut all good?

I am trying to setup the simplest way to notify me when batteries are low (RPIs). I saw you post and “copy pasted it” (but i dont know exactly what i am doing.

i copied your java script code into one of my rules, renamed the group to my own and (for now) disabled the email rule because I have no idea how to make it work - do i need to setup something to be able to send emails via javascript code?

Cna i change somehow the item display name from the label of the item to the “officia” name of the device? (all levels are battery level so i would need to rename them all)

I can send dsl rule - cloud driven notifications to my phone/app but I dont know if htis is doable with Javascirpt? it is it? any pointers?

thanks in advance!

To get the email to work you have to install the mail binding.

If you have copied the code then it should just work. It’s nothing special.

Put the code you have done here in this thread and then someone can see what you have done.

1 Like

@ubeaut - aha mail binding. now i know! (obvious ok! )

I think that the mail binding will have to wait. I found an other way around it and was a little lucky i guess with the testing. here is my working javascript code with Notifications on the openhab app (needs cloud setup) - Group all batteries are in is called Batteries

configuration: {}
triggers:
  - id: "1"
    configuration:
      time: 17:30
    type: timer.TimeOfDayTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >
        var logger =
        Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' +
        ctx.ruleUID);

        var ArrayList = Java.type('java.util.ArrayList'); var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;


        battcheckList = new ArrayList();


        /*

        This will check the percentage of battery left and if it is low then push notify

        Add the items to check into Batteries group

        */


        var whatitis = "";

        var percenttocheck = 15;


        ir.getItem("Batteries").members
                           .stream()
                           .filter(function(batt) {return batt.state.intValue() <= percenttocheck; } )
                           .forEach(function(batt) { whatitis = whatitis + batt.name +": "+ batt.state +"%" + "\r\n"; } );
        if(whatitis != ""){
          battcheckList.add(whatitis);
            logger.info('\r\nBattery low: \r\n' + whatitis );
        }


        //If something in the array then send an email

        if(battcheckList.length !== 0){
          message = "Low Batτερυ: " +battcheckList + " Percentage below " +percenttocheck;
          NotificationAction.sendNotification("XXX@gmail.com",  message);
            logger.info('\r\nArray list: \r\n' + battcheckList );
        }
    type: script.ScriptAction

To test it change
percenttocheck to 115
That way it will log to the log file.

1 Like

By the way if you want the name you gave the item then change:
batt.name
to
batt.label

P.S Did this work for you?

@ubeaut @alexkarageorgis thank you very much for this rule. Just what I was looking for. Works perfectly.

hello everyone. Bringing this back to life as my old Java code stopped working in the new openhab 4.+. I am assuming the new Java version doesnt like my code.

could someone understand what is wrong with this code under the new java?

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
var ArrayList = Java.type('java.util.ArrayList'); var NotificationAction = org.openhab.io.openhabcloud.NotificationAction;

battcheckList = new ArrayList();

/*
This will check the percentage of battery left and if it is low then push notify
Add the items to check into Batteries group
*/

var whatitis = "";
var percenttocheck = 15;

ir.getItem("Batteries").members
                   .stream()
                   .filter(function(batt) {return batt.state.intValue() <= percenttocheck; } )
                   .forEach(function(batt) { whatitis = whatitis + batt.name +": "+ batt.state +"%" + "\r\n"; } );
if(whatitis != ""){
  battcheckList.add(whatitis);
    logger.info('\r\nBattery low: \r\n' + whatitis );
}

//If something in the array then send an email
if(battcheckList.length !== 0){
  message = "Low Battery: " +battcheckList + " Percentage below " +percenttocheck;
  NotificationAction.sendNotification("XXXXX@gmail.com",  message);
    logger.info('\r\nArray list: \r\n' + battcheckList );
}

See below the error



2023-11-13 11:44:09.486 [ERROR] [b.automation.script.javascript.stack] - Failed to **execute** script:

org.graalvm.polyglot.PolyglotException: ReferenceError: "ir" is not defined

at <js>.:program(<eval>:14) ~[?:?]

at org.graalvm.polyglot.Context.eval(Context.java:399) ~[?:?]

at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458) ~[?:?]

at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426) ~[?:?]

at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262) ~[java.scripting:?]

at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]

at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]

at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]

at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]

at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:71) ~[?:?]

at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]

at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:68) ~[?:?]

at org.openhab.core.automation.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1188) ~[?:?]

at org.openhab.core.automation.internal.RuleEngineImpl.runNow(RuleEngineImpl.java:1039) ~[?:?]

at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:381) ~[?:?]

at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:398) ~[?:?]

at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]

at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]

at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]

at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]

at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.4.5]

at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.4.5]

at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.4.5]

at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.4.5]

at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.4.5]

at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.4.5]

at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298) ~[bundleFile:3.4.5]

at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217) ~[bundleFile:3.4.5]

at javax.servlet.http.HttpServlet.service(HttpServlet.java:517) ~[bundleFile:4.0.4]

at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273) ~[bundleFile:3.4.5]

at org.ops4j.pax.web.service.spi.servlet.OsgiInitializedServlet.service(OsgiInitializedServlet.java:102) ~[bundleFile:?]

at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) ~[bundleFile:9.4.50.v20221201]

at org.ops4j.pax.web.service.spi.servlet.OsgiFilterChain.doFilter(OsgiFilterChain.java:100) ~[bundleFile:?]

at org.ops4j.pax.web.service.jetty.internal.PaxWebServletHandler.doHandle(PaxWebServletHandler.java:310) ~[bundleFile:?]

at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[bundleFile:9.4.50.v20221201]

at org.ops4j.pax.web.service.jetty.internal.PrioritizedHandlerCollection.handle(PrioritizedHandlerCollection.java:96) ~[bundleFile:?]

at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:722) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) ~[bundleFile:9.4.50.v20221201]

at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) ~[bundleFile:9.4.50.v20221201]

at java.lang.Thread.run(Thread.java:833) ~[?:?]

2023-11-13 11:44:09.506 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'b0be2cd840' failed: org.graalvm.polyglot.PolyglotException: ReferenceError: "ir" is not defined


This is what I did for OH4:

var datetoday = new Date();
var numberofweek = datetoday.getDay();

/*
This will check the percentage of battery left and if it is low then email
Add the items to check into gBatterycheck group
*/
//DAILY REPORT
var percenttocheck = 15;
var heading="Openhab daily low sensor battery report"

//WEEKLY REPORT
//Only do on Sundays (numberofweek is 0)
if (numberofweek == 0){ 
var percenttocheck = 100;
var heading="Openhab weekly sensor battery report"
}
var email_message = "";

var i;
var sendmail="";


var i_list = items.getItem("gBatterycheck").members;
for (var i_index in i_list) {
  i = i_list[i_index];
  if(i.state <= percenttocheck){sendmail="YES"
//  console.info('Test',i.label,i.state);
  email_message = email_message + i.label + " " + i.state + "%" + "\r\n";
                               }
}
if(sendmail=="YES"){
  message = "<H2>Sensor battery report <br>" +email_message +"</H2>" + "Percentage check was set at " +percenttocheck;
  actions.get("mail", "mail:smtp:a514b96247").sendHtmlMail("you@gmail.com", heading, message );
  console.info(email_message);  
}


1 Like

I have implemented this way:


var whatitis = "";
var percenttocheck = 15;

        items.getItem("Batteries").members
           .filter(function(batt) {return batt.state == percenttocheck ; } )
           .forEach(function(batt) { whatitis = whatitis + " " + batt.label + " "; } );
        if (whatitis != "") {
          battcheckList.add(whatitis);
        }
1 Like

hey michaeljoos - i copy pasted and it works now :slight_smile: something with the IR part of my own didnt work. from a logic perspective you use == vs <= which is my implementation. basically you run so that you get an alert when it become 15 right? but what happens if for some reason a battery drops more than than one unit? wont you miss the alert?

Did you try the code I posted?
I run it once a day.
It will send an email if the battery is less than or equal to 15% and on Sunday it sends a list of all the batteries in the battery group.
It is for OH4 which is what you said you are using.

hi Greg,

thanks for sending it through. I went through it. however your code is email based and i prefer the notification element offered by my code (it sends an alert to the openhab app) - so i just adjusted my own with the part offered by michaeljoos

just playing with the logs now to wrap up.

Ok, thanks for the reply.
You can put your notification in the code below instead of using email.
Anyway if it works for you that is good.

1 Like

For future readers of this thread, there is a rule template on the marketplace that handles this use case.

  1. Install the rule templated from the Automation menu.
  2. Create a Rule of Script that sends the alert. This is where you send the alert. I use the following:
configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >
        var {alerting} = require('rlk_personal');

        var logger = log('Low Battery');

        alerting.sendAlert('The following batteries are below 10%: ' + this.threshItemLabels, logger);
    type: script.ScriptAction

  1. Instantiate a new rule using Threshold Alert as the template. I use the following configuration.
Parameter Purpose Value
Triggering Group Group with all the battery level Items AllBatteries
Threshold State The threshold value to compare against, if the value is < this the alert rule is called. The rule handles UoM 15 %
Comparison Operator If Item State <operator> Threshold is true, the alert rule is called. <
Alert Rule UID of the rule created in step 2. battery_alert
Do Not Disturb Do not call the alert rule between these two times, wait until the end of the time period. This prevents alerts from being sent over night. Start: 22:00, End: 08:00
Rate Limit Do not call the alert rule more often than this. For example, don’t alert more than once every eight hours. PT8H

All the other properties are left at the defaults.

This rule template is pretty handy. I use it to send an alert when a door is left open too long, when a battery goes low and needs to be replaced soon, a sensor stops reporting, a service goes offline, and as a motion sensor timer.

2 Likes