rkoshak / openhab-rules-tools

Library functions, classes, and examples to reuse in the development of new Rules.
Other
62 stars 23 forks source link

[openhab-js 4] Review for breaking changes and update syntax #78

Closed rkoshak closed 1 year ago

rkoshak commented 1 year ago

I don't think there are any breaking changes but I only did a cursory look. This is to remind me to look more carefully.

I also need to update the syntax to take advantage of changes in the openhab-js library.

I do not intend this to apply to rule templates which I'm doing one by one independently from the library.

See https://github.com/rkoshak/openhab-rules-tools/pull/107 .

rkoshak commented 1 year ago

There are no breaking changes at least up through openhab-js 4.2.0 and in fact all the Timer classes depend on 4.2.0 for certain when types. I still need to do the syntax update though so I'll keep this open for now.

einalex commented 1 year ago

I think something broke your dadMotionTimer example. timers.check dies for me. It seems like it tries to convert ZDT to ZDT.

2023-07-29 09:40:50.174 [ERROR] [b.automation.script.file.bathroom.js] - Failed to execute rule BathroomLightOff: Error: "2023-07-29T09:41:00.153Z[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime: Error: "2023-07-29T09:41:00.153Z[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime at toZDT (/node_modules/openhab.js:2) at check (/etc/openhab/automation/js/node_modules/openhab_rules_tools/timerMgr.js:46)

rkoshak commented 1 year ago

Which version of the openhab-js library are you using? toZDT lives there now and there were some changes made so that it should support RFC type strings like that now. Recent versions have a utils.OPENHAB_JS_VERSION that contains the library version.

As for "dadMotionTimer", are you referring to a specific tutorial or design pattern, or an example that comes from the Threshold Alert rule tempalte?

einalex commented 1 year ago

Which version of the openhab-js library are you using?

latest available via npm I think, variable says 4.5.0

As for "dadMotionTimer", are you referring to a specific tutorial or design pattern, or an example that comes from the Threshold Alert rule tempalte?

code copied from the discourse thread where the tools were presented.

rkoshak commented 1 year ago

code copied from the discourse thread where the tools were presented.

That's where I'm confused as there is no mention of "dadMotionTimer" at https://community.openhab.org/t/announcing-the-initial-release-of-openhab-rules-tools/132032

I don't know where else I've written that example by that name so 🤷 .

einalex commented 1 year ago

https://community.openhab.org/t/some-js-scripting-ui-rules-examples/131305#dad-motion-11 this one

rkoshak commented 1 year ago

OK, thanks, that helps. That example is approaching two years old now. A lot has changed in both openhab-js and OHRT since then and I've moved pretty far beyond that implementation now. I forgot I even made that post. And those examples were never intended to remain working forever.

Error: "2023-07-29T09:41:00.153Z[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime: Error: "2023-07-29T09:41:00.153Z[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime

There was a bug in openhab-js which has since been fixed where time.toZDT() was not correctly identifying DateTime Items. It has been fixed in the latest version of the library (a few version back at this point actually) and a simple work around is to pass the state of the Item instead of the Item itself.

These days I implement this using the Threshold Alert rule template: https://community.openhab.org/t/threshold-alert-and-open-reminder-4-0-0-0-4-9-9-9/144863 with the following config (the second or third post on that thread has a full explanation of this config):

configuration:
  dndEnd: 08:00
  rateLimit: ""
  invert: false
  initAlertRule: ""
  dndStart: 22:00
  alertRule: dad_motion_alerting
  endAlertRule: dad_motion_alerting
  thresholdState: ON
  defaultRemPeriod: PT8H
  operator: ==
  hysteresis: ""
  reschedule: true
  namespace: thresholdAlert
  gkDelay: 0
  defaultAlertDelay: PT8H
  group: MotionSensors

and the dad_motion_alerting rule is

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        if(isAlerting) console.log('It has been a long time since dad moved.');

        else console.log('Motion detected at dad\'s house after alerting.');
    type: script.ScriptAction

I'm just going to do a quick and dirty edit on that example here adopting it to changes made in the openhab-js library. But I'm just typing in the changes without testing. There almost certainly will be typos. But it should show what needs to change.

configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: Dads_Motion_Timeout
    type: core.ItemStateChangeTrigger
  - id: "4"
    configuration:
      startlevel: 100
    type: core.SystemStartlevelTrigger
  - id: "1"
    configuration:
      itemName: MotionSensor_LastMotion
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >
        var {alerting} = require('personal');

        var {timeUtils, TimerMgr} = require('openhab_rules_tools');

        var formatDT = function(dt) {
          // TODO revist after this gets fixed in the library I don't know if this ever got fixed
          // See https://js-joda.github.io/js-joda/class/packages/core/src/ZonedDateTime.js~ZonedDateTime.html 
          // for details. I'm assuming this works but have never tested it
          return time.DateTimeFormatter.ofPattern("h:mm a, ccc MMM dd").format(time.toZDT(dt)); // I've not tested the pattern
        }

        var dadTimerExpiredGenerator = function(){
          return function() {
            if(items.Dads_Motion_Alert.state == 'ON') {
              alerting.sendInfo('It has been awhile since dad has moved. Last movement detected at ' 
                                + formatDT(items.MotionSensor_LastMotion));
            }
            else {
              console.info("Dad's motion timer went off but alerting is OFF");
            }
          }
        }

        // Initialize variables

        var timers = cache.get(ruleUID+'_tm', () => TimerMgr());

        var lastMotionItem = items.getItem('MotionSensor_LastMotion');

        var timeout = items.getItem('Dads_Motion_Timeout');

        var motionTime = (lastMotionItem.isUninitialized) ? time.toZDT()  : time.toZDT(lastMotionItem.state);

        var motionTime_Str = formatDT(motionTime);

        var timerHours = 12;

        // If it's not set move the timout to 12 hours

        logger.debug('Timeout is {}', timeout.state);

        if(timeout.isUninitialized) {
          timeout.postUpdate('12');
        }

        else {
          timerHours = timeout.state;
        }

        // Calculate the alert time, move it to tomorrow if it'll go off at night

        var timerTime = motionTime.plusHours(timerHours);

        var nightStart = time.toZDT('22:00');

        var nightEnd = time.toZDT('09:00');

        if(timerTime.isBetweenTimes(nightStart, nightEnd)) {
          timerTime = nightEnd;
        }

        var timerTime_Str = formatDT(timerTime);

        // If it's already been too much time, alert now

        if(timerTime.isBefore(time.toZDT())) {
          console.warn('timerTime is in the past! {}', timerTime_Str);
          dadTimerExpiredGenerator()();
        }

        // Set or reschedule a timer to alert if there is no motion for too much time

        else {
          console.debug("Motion detected at Dad's at {} or reminder time has changed, setting reminder to expire at {}",
                      motionTime_Str, timerTime_Str);
          timers.check('alertTimer', timerTime, dadTimerExpiredGenerator(), true);
        }
    type: script.ScriptAction
rkoshak commented 1 year ago

I've run with #107 for awhile and it all checks out. I'm closing this now.