Openhab burglar alarm diy! not a real one!

well in its state right now people that actually use this know the risks but indeed if i finish it and ask for putting it in the marketplace i will. The most insecure part so far seems the pin code in plain text any ideea how to encrypt and decript on the fly something like ?

:partying_face:

However, if you want to create an installable rule template, you’ll need to write this as a UI rule. At least for now, only managed rules support templates.

OH doesn’t have support for avoiding that yet. But there is a PR to add a crypto-store which is the first step.

Some comments on the rule:

Overall this looks a lot like a Nashorn rule that has been lightly adjusted to JS Scripting. There are a number of simplifications that could be added if the full openhab-js library were used.

This is only needed if one has changed the default setting of the JS Scripting addon to “Do not use built in variables”.

Because for managed rules any one rule could have multiple actions and conditions, requiring an import for every one is a larg burden.

All of this is (and should be) accessed through the openhab-js library. See JavaScript Scripting - Automation | openHAB

When/if you make a template these would become properties the user fills out while instantiating the rule from the template.

Note in a managed Script Action/Condition, because the context is reused from one run to the next, you cannot use const and let in the root context of the Script. You can use them inside of functions though.

When/if this becomes a template, most of these should become debug.

In JS Scripting the state is already a String. It’s redundant to call toString() here.

Also, in OH 3.4 + you can use the shorter options:

let alarmState = items.Alarm_State.state;
let alarmState = items["Alarm_State],state;

openhab-js has a time.toZDT() function which is a factory for ZonedDateTime Objects. If you pass it no arguments it will return now.

Using the js-joda classes it’s better to use Duration. At least you don’t need to mess with ChronoUnit.

    let stateChangeTime = detectorItem.lastUpdate;
    let elapsedTime = time.Duration.between(stateChangeTime, now).abs();
    return elapsedTime.compareTo(time.Duration.ofSeconds(5)) >= 0;

There are other ways to do this too.

So for now it’s only possible to have one rule per rule template. However, looking at the triggers here it’s definitely possible to combine this all into one rule and have different paths through the rule depending on what triggered the rule. For an example of this sort of thing look at my 4.0+ rule templates (e.g. MQTT Event Bus).

For a managed rule, use the shared cache for “global” variables. Use the private cache for variables that are only used by one rule but which need to persist from one rule to the next.

If you want to prevent rules from running until runlevel 100, see Delay Start [4.0.0.0;4.9.9.9].

Debounce [4.0.0.0;4.9.9.9] could be used for this part. That might simplify this rule significantly. Essentially, with a debounce on the door Items, this rule won’t even trigger until the door has been open for five seconds, saving you from even needing to check for that with timers and such.

Threshold Alert and Open Reminder [4.0.0.0;4.9.9.9] might also be used here but that would require a whole new approach for this capability. But again, the rule wouldn’t even be called until the door has been open for longer than five seconds. The advantage here though is there is a do not disturb period which could be useful and or enhanced to take into account the armed/disarmed status.

Well then I will have to rethink all of this but after I finish it in it’s wctual state to see if the logic is there.

Here I am struggling because most of the code you see is by looking in the forums…

Interesting but what happens if someone do not use the building settings of openhab. Have to test this…

Yes I know about it but still reading again I used examples on the forums.

I forgot about that

I am so ashamed now I should not even post this :pensive:. I still have a lot to learn.

Here is what I will take into consideration what you suggested and work my way slowly up.

The first place to look should always be the docs. The JS Scripting docs are very complete and very thorough.

If someone has disabled that setting, your rule will run. If that setting hasn’t been disabled then that require line is redundant at best. The openhab-js library is pretty robust so there shouldn’t be any issues requiring it twice.

For managed rules though, the need to require everything becomes a huge burden so managed rules users will rarely if ever disable that setting. All of my rule templates assume the library is automatically imported.

Examples in what language? Because Nashorn JS and JS Scripting are very different and Nashorn JS is going to be way more complicated and way longer because there it doesn’t have a helper library.

Always follow the docs. When adopting code from the forum, always look it up in the docs. The docs should be your primary resource for how to do something in JS Scripting. Code from the forum should always be compared to the docs and adjusted accordingly.

Do not be ashamed and don’t think this is bad code. This is how we learn. The most important thing that even professional programmers go through is a code review, You’ll learn more from one good code review than you’ll learn from reading a couple books cover to cover.

I don’t think that will work. If the user has the library automatically imported, this line will overwrite all the imports. It’s probably safe to just assume the setting is enabled or disabled. If you have file based rules it’s probably going to be the case that it’s disabled so include the require. If it’s a managed rule, it’s safe to assume that the library is injected automatically so just leave that out, or if you want to be really nice to your users print a meaningful error.

if(this.items === undefined) {
  console.error('openhab-js is not imported. Make sure "Use built in variables" is enabled');
  process.exit(1); // does this work? That's something I didn't know and could come in handy
}

Given the semantic model and linking process, it is almost always going to be easier to create Items using “Create equipment from Thing” so I’m not sure creating the Items in the rule is going to save the end user that much effort.

But, having said that, one trick I use is if the rule is triggered manually I’ll do setup and configuration steps and when the rule is triggered other ways I’ll do something else. You can tell if the rule was triggered manually if this.event === undefined.

And since this rule is triggered by system startup that’s probably not necessary. But if you were to combine the rule into one that’s one way to separate the configuration checking from processing.

Wouldn’t it make sense to rely on restoreOnStartup to return the Item to what ever state it had before OH rebooted? Maybe not Alarm_Countdown but the alarm state and times could easily be restored. Then you wouldn’t lose state to a restart.

Note, null and NULL are not the same and neither is undefined and UNDEF. null means that a variable has no value. undefined means the variable doesn’t even exist. NULL is a special state that an Item can carry meaning it’s not be initialized. UNDEF means the binding cannot determine the proper state for the Item.

One other gotcha is that triggeringItem is a Java Item Object, not the JS Item Object so it’s .state is going to be a Java State Object, not a String. But you are ahead of that problem with the way you defined triggeringItem.

Rewriting this as is but corrected it would be

if(triggeringItem.state == "NULL" || triggeringItem.state == "UNDEF" || alarmStateItem.state == "NULL" || alarmStateItem.state == "UNDEF)

but, looking at the JS Scripting docs we see there is a better way.

if(triggeringItem.isUninitalized || alarmStateItem.isUninitialized)

You don’t have to prepend “alarm_system_log =” to all your logging statements. Instead you can change the name of the logger. That’s even better because it means it’s easy for you to control that logger from log4j2.xml or the karaf console.

console.loggerName = 'org.openhab.automation.alarm_system_log.'+ruleUID;

The ruleUID is a good idea but not necessary. It will help you separate logs based on the rule they come from. But of course you’d have to have that line in each rule to work. If you omit the ruleUID than you can put that line at the top of the file.

Hello i made some progress on the script i updated my first post. Its been running for a week without issues i even added an auto arm function also tried to arrange the code a little bit better. I hope i implemented all your suggestions. Do you mind having a look again into it for me its been working on openhab 4.0.2. Now i looked a little bit into the template scripting but oh my to get all that working will be a something else. The most important is that the script is in working state wich for me it is i know its not following all the documentation of the js addon but the most important is that it works in a safe manner i hope. Now i will start poking around to see if i can get it into a template and start using your library to simplify the code. Wish me luck.

See How to write a rule template. Once the code works you’ve done the hard part. The rest is really mostly just formatting and tedium.

I’ve been pushing here and there to make the experience better but it’s slow going.

At a high level, make the rule a managed rule. Add the “configDescriptions” section for all the parameters you want the end user to be able to set. See [Do not install] Rule Template Parameters Experiments and Examples for how to define parameters of various types (e.g. an Item, Thing, Channel, boolean, etc.). In the actual rule, you’ll add {{ param }} where “param” is the name of the parameter. At instantiation time those will be replaced with what the user selected/entered in the config.

All this checking is probably unnecessary. The openhab library comes with the add-on. If this code is going to be able to run at all in the first place, openhab is going to be there. So this can all safely be reduced to

const { items, rules, log, triggers, actions } = require('openhab');

As mentioned before, when/if this becomes a template, these will be populated from the parameters.

Note, a trick that has served me well in my rule templates is to assign all the parameters to constants/variables at the top of the scripts. That way you can update the template easily by just replacing everything below the definition of the variables without needing to remember where something needs to be replaced with a {{ param }}.

When moving to a rule template you’d want to store these in the cache. If you manage to merge all the rules into one rule you can use the private cache. If multiple rules you’ll want to use the shared cache.

If you use the cache, you should use openHAB timers instead of JS timeouts as then when the rule gets reloaded, the existing timers will be cancelled automatically for you instead of becoming orphaned and continuing to run in the background.

I always get a little weary when I see locks in code like this. I’m not saying it’s not appropriate here but it does raise flags.

It might be better to utilize a ReentrantLock but I’m not sure that’s possible in JS Scripting given it’s single threaded access requirements.

Because a failure to release the lock can have devastating impacts on these rules, you should always make sure the lock is released by using try/catch/finally and release the lock in the finally. NOTE: I don’t know if this is true for JS Scripting but in Rules DSL, not all errors that occur are catchable meaning even then you might end up with a lock that is forever locked and can never be unlocked until a restart.

In general, it’s better to rely on the fact that only one instance of a given rule can run at a time. That will mean all this locking stuff will come to you for free. But that requires merging the rules into one rule.

The openhab_rules_tools library has a built in countdown timer. That can free up a lot of code here. openHAB Rules Tools Announcements OHRT is available through openHABian or via npm.

I think all of this logic could be managed through Debounce [4.0.0.0;4.9.9.9] and a Group. You’d debounce the motion sensors and have them a member of a Group with a count aggregation function. You can check if Group’s state > 1 to trigger the alarm.

Be careful here. If you have a Thing that never comes online your system start levels will stall out at level 60. You wouldn’t want your alarm system to be disabled just because a Light is broken.

When pondering whether to combine these rules into one rule, you can distinguish between the various events. For example, my debounce rule template:

       if(this.event === undefined) {
          init();
        }

        else {
          switch(event.type) {
            case 'GroupItemStateChangedEvent':
            case 'ItemStateEvent':
            case 'ItemStateChangedEvent':
            case 'ItemCommandEvent':
              debounce();
              break;
            default:
              init();
          }
        }

In that case if it’s an Item event I run the rule. If it’s not an item event, I just validate the configuration of the Items and the rule.

This should never happen. The rule should only be triggered when it’s newly loaded and the start level has passed. If the rule is newly loaded it means there is nothing left over from the previous rule. If by chance alarmSoundTimeoutId isn’t null it means that one of your other rules somehow manged to run before this system started rule and that means your whole situation just got a lot more complicated.

If might be a better approach to have a rule trigger at startlevel 40 that disables the rules that should run and then only after this rule runs reenable them. That would prevent the situation sited above in almost all cases (not all). There’s a rule template for that too. :wink: Delay Start [4.0.0.0;4.9.9.9]

In a managed rule, it’s not running in a function so return doesn’t work. You’d have to use process.exit(1).

The state of an Item will never be null and never be undefined. They will be NULL or UNDEF. They are not the same. This is a bug. Use

if(triggeringItem.isUnintialized || alarmStateItem.isUninitialized)

Over all I see a good deal of duplicated code. Consider applying Design Pattern: How to Structure a Rule so some of these to avoid a lot. For example, when you have a case where you have a lot of sending of broadcasts, you should only have one line that actually sends the broadcast. The rest of the code just creates the message.

I bring up the rule templates and library not to encourage you to use them (though that would be good too) but as another example of a way to achieve the same goal.

Apreciate the time given to check. Mostly you are pointing to the same issues as mentioned in the earlier posts. I am trying every evening to read a little bit that is how I discovered the lock and all that and by trial and error I added modified etc always testing all possible scenarios. So far it passed. But I agree it’s ugly and repetitive Wich is why I want to use your library and try to merge this in one rule but I lack the brain capacity.

Well all this probably I will remove but will all the experiments I do sometimes I forget that JavaScript is not installed etc.

This I will probably implement I just started reading about cache and also openhab timers but I will probably for timers end up with your library for it.

Hmm I will have to look more into this.

The whole script is made to go in disarm state because like I said this is not a real alarm and we don’t want to annoy the neighborhood for false alarm.

You already mentioned this part but forgot to modify. So rry

Well that is the whole point now for me to start using your library to not have to deal with all that but first I need to make sure the original ideea seems good.

Anyway thank you again for the points.

Sorry for the slow reply. I’ve been away from home, and unable to reply in detail.

My previous post was actually referring to the fact that a DIY alarm system is almost never as robust as a purpose-built system. If someone is extremely concerned about home security, they should pay for a professionally installed and monitored solution so that humans can make informed decisions when the alarm is triggered or the power is cut.

Most people don’t really need that level of security. If you just want to monitor your doors/windows and maybe scare off random burglars, DIY is fine, but it’s not going to stop someone who is determined to get into your home.

So as I said before, anyone who goes the DIY route needs to understand the limitations of their system. It’s not PIN codes and hacking that you need to worry about–that’s an entirely different security concern–it’s people cutting your power/telecom, or breaking a window and silencing the alarm before the neighbours get concerned.

I’m cautious about DIY alarm systems (including any off-the-shelf, unmonitored products) giving people an illusion of safety and security that isn’t really there. It’s important to understand that they’ve reduced some risk, but perhaps not as much as they think.

That’s just my opinion, though. If you disagree, feel free to ignore what I’ve said if/when you share your final solution.

Oh but I agree with you fully by trade I am somewhere in that field. The whole point here is to have something than nothing. Now maybe it creates the illusion of safety but I believe common sense should apply in this case.
Now based on my knowledge of pro alarm systems and electrical background I could say with confidence that even the most complex ones can be bypassed in a quiet manner no matter how well the siren is build or if you have 2 telephone lines connected and two simcard and lan and lorawan etc and the most sofisticated dual sensors and all kind of trickery with specific resistance values monitoring dual battery backup etc If there is a will and some planning you can be inside the house taking a selfie with the alarm centrale within 1 minute :wink:
But in real life it’s your friendly burglar with not prior knowledge so for that my script will be more than sufficient to scare him and me to get an notification.

Hey Rich,
I tried that one but brings up the following error message:

TypeError: (intermediate value).exit is not a function

OP’s code is the first I’ve ever seen that used so I was just assuming it worked. I never tried it myself. It may not work at all.

Until now I need to use an ugly workaround for an early exit

throw new Error;

so I was hoping that I missed anything to make process.exit work.

Less ugly work arounds include:

  • put the early exit in a rule condition (all managed rules or JS Scripting rules using rule builder and .if(fn))
  • Put the script action into a function.
(function(event) {
    // script code goes here
})(event)
  • restructure your if statements so that they test for whether to do something instead of testing for whether to not do something.

Unfortunately, common sense often isn’t as common as we tend to think it is.

I’m not questioning your understanding–like I said, it’s clear that you get it. But I caution against assuming that other people will get it. So, all I’m suggesting is that you include some commentary speaking to the weaknesses. They may not read it, but then you at least know you’ve said it and haven’t contributed to their false sense of security.

From what I can see most people here in the forums are knowledgeable enough to discern. Looking at the neighbors alarmo I don’t see any warning on the contrary they make it look like a robust alarm. But just out of curiosity do you have something similar in your setup for security or a real hardwire/wireless dedicated alarm?

Every once in awhile we get someone coming here asking how to build an alarm system from openHAB. They are not knowledgeable enough to discern, and we steer them toward professional alarm systems. :wink:

Anyway, I’ll leave it at that. I’ve just learned to not make assumptions about what other people do and do not know.

I live on an upper floor of a condo building with a controlled entrance, so all I have is a Z-Wave door lock. But it’s mostly there to automatically lock when I go to bed.

I know that openhab at a certain point had a python implementation but that did not get any traction. The point is am I am trying to solve a problem Wich requires a solution. Now should I get a proper alarm system ? Why when verisure and other companies make a business deceiving people into thinking they get an profesional system ! The other option is a company that at best you pay a yearly contract to get someone to call you to check if it’s a false alarm and else calls the police. The thing is if an idiot manages to get a driving licence but fully ignores what alcohol does what then should we not make cars anymore or only sell them to profesional drivers and hire one to drive us ? I am not trying to argue with you or put the community on my head but allot of things in life that are not used knowing the limitations or risks lead to bad results but in the right hands become useful tools.
And to close it imagine you get hold of some screwdrivers and start messing in the electric panel and you get yourself hurt who is to blame there you or the electrician?
Again I really apreciate the comments and just for fun the other weeks I had to fix a problem when the wife unknowingly used the VELUX shutters using openhab but the window was open towards outside (who fault is it the VELUX binding openhab or mine? ).

You mean a Python implementation of alarm rules? There’s never been an implementation of OH in Python. IIRC there was an implementation of some rules in the third party openHAB Helper Libraries in Jython but that library never did become an official part of the OH project.

OH should have known that the window was open and prevented the shutters from closing, issuing an alert of some sort for why. :wink:

Exactly

For future reference this is the fix that I did and made me start learning JavaScript because I also had knx button controlling the VELUX channels so the only solution was to disable the channel completely

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Dressing_top_window
    type: core.ItemStateUpdateTrigger
  - id: "3"
    configuration:
      itemName: Master_top_window
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: |
        // UIDs of the Things 
        var dressingThingUID = 'velux:window:hub:Master_roller_dressing';
        var masterThingUID = 'velux:window:hub:Master_roller_bedroom';

        // Fetch the state of Dressing_top_window and Master_top_window
        var dressingWindowState = items.Dressing_top_window.state;
        var masterWindowState = items.Master_top_window.state;

        // Control the Dressing Thing based on Dressing_top_window's state
        var dressingThing = things.getThing(dressingThingUID);
        if (dressingWindowState === "OPEN") {
            dressingThing.setEnabled(false);
        } else if (dressingWindowState === "CLOSED") {
            dressingThing.setEnabled(true);
        }

        // Control the Master Thing based on Master_top_window's state
        var masterThing = things.getThing(masterThingUID);
        if (masterWindowState === "OPEN") {
            masterThing.setEnabled(false);
        } else if (masterWindowState === "CLOSED") {
            masterThing.setEnabled(true);
        }
    type: script.ScriptAction

If the person never consulted an electrician, then they’re to blame (since there’s literally no one else to assign blame to). If an electrician gave them a screwdriver and instructions for messing around in the electric panel–without talking about the dangers–then the electrician is to blame.

In this metaphor, you are the electrician. Hence my suggestion to accompany your instructions with some commentary on the limitations of a DIY alarm system. Again, it’s fine if you don’t want to do that. I just don’t see a reason not to.