Individual Alarm clock for each Day of week, with adjustable duration

Tags: #<Tag:0x00007f74617e6288>

Hello,

I needed an alarm clock with these features:

  • Set different alarm times for each day of week, with adjustable “run time” (so to turn of stuff after a while)
  • Define presets for Hour,Minute,Runtime (e.g. for people working early/late shifts)
  • Must be usable for several family members if need be
  • ability to individually greet persons once they reach the kitchen or bathroom :slight_smile:

So here is what i came up with. It includes all items, rules,sitemaps and HABPanel Widgets needed for a single person. Maximum runtime is 180 Minutes with 10 Minute increments; change values as desired. If you want to reuse, basically you only need to copy&paste the items,rules and sitemap and replace PERSON1 with “dad” “mum” “kid” or whatever fits. Special case is the rule “react to move” where you would need to cut&paste only the If-clause and amend “PERSON1_WECKER_AKTIV.state” accordingly. So without further much ado here it is:
Items:

Group gPERSON1Wecker
//Wecker Montag
Switch PERSON1_WECKER_MO      "Wecker Montag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_MO_H    "Weckzeit Montag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_MO_M    "Weckzeit Montag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_MO_RUN    "Wecker Montag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Dienstag
Switch PERSON1_WECKER_DI      "Wecker Dienstag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_DI_H    "Weckzeit Dienstag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_DI_M    "Weckzeit Dienstag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_DI_RUN    "Wecker Dienstag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Mittwoch
Switch PERSON1_WECKER_MI      "Wecker Mittwoch"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_MI_H    "Weckzeit Mittwoch Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_MI_M    "Weckzeit Mittwoch Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_MI_RUN    "Wecker Mittwoch Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Donnerstag
Switch PERSON1_WECKER_DO      "Wecker Donnerstag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_DO_H    "Weckzeit Donnerstag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_DO_M    "Weckzeit Donnerstag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_DO_RUN    "Wecker Donnerstag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Freitag
Switch PERSON1_WECKER_FR      "Wecker Freitag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_FR_H    "Weckzeit Freitag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_FR_M    "Weckzeit Freitag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_FR_RUN    "Wecker Freitag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Samstag
Switch PERSON1_WECKER_SA      "Wecker Samstag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_SA_H    "Weckzeit Samstag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_SA_M    "Weckzeit Samstag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_SA_RUN    "Wecker Samstag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
//Wecker Sonntag
Switch PERSON1_WECKER_SO      "Wecker Sonntag"                  <clock>	(gPERSON1Wecker)
Number PERSON1_WECKER_SO_H    "Weckzeit Sonntag Stunde [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_SO_M    "Weckzeit Sonntag Minute [%s]"    <calendar>	(gPERSON1Wecker)
Number PERSON1_WECKER_SO_RUN    "Wecker Sonntag Laufzeit [%s]"    <clock>	(gPERSON1Wecker)
Switch PERSON1_WECKER_AKTIV "Wecker PERSON1 Aktiv" (gPERSON1Wecker)
Number PERSON1_WECKER_PRESETS "Wecker Preset Laden"				<calendar>	(gPERSON1Wecker)

Rules:

// Lucky OH2 users can remove these pesky imports
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.openhab.action.squeezebox.*
import org.joda.time.*
import org.openhab.model.script.actions.Timer
//end of removable imports for OH2 users

var Timer timerPERSON1Wecker = null

rule "Person1 preset laden"
	when
		Item PERSON1_WECKER_PRESETS received command
then
	var int sollMinute
    var int sollStunde
    var int RunTime
	switch (PERSON1_WECKER_PRESETS.state){
		//early shift
		case 1:{
			sollStunde=4
    		        sollMinute=0
    		        RunTime=40
		}
		//late shift
		case 2:{
			sollStunde=10
    		        sollMinute=0
    		        RunTime=40			
		}
	}
	//set hour,minute,runtime items for each day
	var i = 0
	var DerTag= "NA"
        //don't change saturday/sunday. set i max=7 and uncomment cases if you have to work weekends, you unlucky soul
	while ((i=i+1) <= 5) {
    	switch i{
        	case 1: DerTag = "MO"
        	case 2: DerTag = "DI"
        	case 3: DerTag= "MI"
        	case 4: DerTag= "DO"
        	case 5: DerTag= "FR"
        	//case 6: DerTag= "SA"
        	//case 7: DerTag= "SO"
    	}
    	//set all hours
    	gPERSON1Wecker.members.filter(s|s.name == "PERSON1_WECKER_"+DerTag+"_H").forEach[s | s.postUpdate(sollStunde)]
    	//set all minutes
    	gPERSON1Wecker.members.filter(s|s.name == "PERSON1_WECKER_"+DerTag+"_M").forEach[s | s.postUpdate(sollMinute)]
    	//set runtimes
    	gPERSON1Wecker.members.filter(s|s.name == "PERSON1_WECKER_"+DerTag+"_RUN").forEach[s | s.postUpdate(RunTime)]
	}
end


rule "Wecker PERSON1"
    when
        Time cron "0 0/1 * * * ?"
    then
    var Heute = ""
    var boolean Wecken = false
    var int sollMinute
    var int sollStunde
    var int RunTime
    
    switch now.getDayOfWeek{
        case 1: Heute = "MO"
        case 2: Heute = "DI"
        case 3: Heute= "MI"
        case 4: Heute= "DO"
        case 5: Heute= "FR"
        case 6: Heute= "SA"
        case 7: Heute= "SO"
    }
    if (gPERSON1Wecker.members.filter[s | s.name == "PERSON1_WECKER_"+Heute].head.state == ON) {
		sollMinute = (gPERSON1Wecker.members.filter[s | s.name == "PERSON1_WECKER_"+Heute+"_M"].head.state as Number).intValue
		sollStunde = (gPERSON1Wecker.members.filter[s | s.name == "PERSON1_WECKER_"+Heute+"_H"].head.state as Number).intValue
		RunTime = (gPERSON1Wecker.members.filter[s | s.name == "PERSON1_WECKER_"+Heute+"_RUN"].head.state as Number).intValue
		Wecken = true
	}
 
		if (sollMinute == now.getMinuteOfHour && sollStunde == now.getHourOfDay && Wecken==true) {
                        //WECKER_AKTIV to ON, so you can react to a move sensor downstairs or in the kitchen
                        // i use this for a squeezeboxspeak "Good morning. it is soandsomuch degrees"
                        // see rule "react to move"
                        //remove all references if you don't want/need this
			sendCommand(PERSON1_WECKER_AKTIV,ON)	
			//Do Stuff here, like turn on lights,coffee machine and/or radio
			
			timerPERSON1Wecker = createTimer(now.plusMinutes(RunTime)) [|
				//turn lights,radio etc. of after defined time for day.
                                //sanity check turn off wecker_aktiv
				sendCommand(PERSON1_WECKER_AKTIV,OFF)
				timerPERSON1Wecker = null
				]
		}
end

rule "react to move"
when
	Item your_move_sensor received update 1
then
	
	//PERSON1 reached first floor/kitchen
	if (PERSON1_WECKER_AKTIV.state==ON){
		
		// example for squeezebox squeezeboxSpeak("Your_box", "Guten Morgen PERSON1. Es sind "+String::format("%.2f", (Terasse_temp.state as DecimalType).floatValue())+" Grad.", 80, true)
		//wecker_aktiv to off, else speech would be repeated each time sensor is triggered
		sendCommand(PERSON1_WECKER_AKTIV,OFF)
	}
end

Sitemap:

sitemap PERSON1 label="PERSON1 Wecker"
{
        Frame{
                 Switch item=PERSON1_WECKER_PRESETS  mappings=[1="EARLY", 2="LATE"]
        }
	Frame {
		Switch item=PERSON1_WECKER_MO        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_MO_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_MO_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_MO_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_DI        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_DI_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_DI_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_DI_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_MI        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_MI_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_MI_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_MI_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_DO        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_DO_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_DO_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_DO_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_FR        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_FR_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_FR_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_FR_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_SA        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_SA_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_SA_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_SA_RUN    minValue=10 maxValue=180 step=10
	}
	Frame {
		Switch item=PERSON1_WECKER_SO        mappings=[ON="AN", OFF="AUS"]
		Setpoint item=PERSON1_WECKER_SO_H    minValue=0 maxValue=23 step=1
		Setpoint item=PERSON1_WECKER_SO_M    minValue=0 maxValue=55 step=5
		Setpoint item=PERSON1_WECKER_SO_RUN    minValue=10 maxValue=180 step=10
	}
}

Three things to add: First, this will work well on OH1.8(use Sitemap) and OH2 (use either Sitemap or HABPanel). Second,If you run several alarms, you might want to add a sanity check in PERSONx rule “Wecker PERSONx” if another timer is allready running and cancel it; i do this like so:

//cancel PERSON1 Timer if running...
			if (timerPERSON1Wecker != null){
				
				timerPERSON1Wecker.cancel()	
				timerPERSON1Wecker = null
			}

Third&last, you should persist all items (gPERSON1Wecker* : strategy = everychange, restoreOnStartup) and initialise all days even if you don’t need them or you will get errors in your logs.
I hope you find this useful.
The result looks something like so:


Best regards,
-OLI
Edit: code shortened quite a bit by using group concepts.
Update: I have created Widgets(“Wecker” which can be configured per day and “WeckerSettings” for handling Presets) for HABPanel that can be used with these Item/Rule sets.
They look like this:

Clicking the Green Button toggles alarm for the day ON or OFF (turns Red when OFF)
The WeckerSetting-Widget will show and load the preset that is currently not active.
“Wecker” has these settings:
WeckerSettings.widget.json (1.1 KB)
Wecker.widget.json (2.9 KB)

6 Likes

I think the only changes needed for OH 2 is removal of all the imports. Everything else looks OK from an OH 2 perspective.

Applying some of the concepts here and here I think the logic above can be made a little simpler.

First add all the current Items to a Group, I’ll call it gAlarm.

var Timer timerPERSON1Wecker = null

rule "Wecker PERSON1"
when
    Time cron "0 0/1 * * * ?" // note, one could use an external event to trigger the rule if the alarm is implemented elsewhere (e.g. Tasker)
then
    val person = "PERSON1_WECKER_"

    var dayName= "NA"
    switch now.getDayOfWeek{
        case 1: dayName = "MO"
        case 2: dayName = "DI"
        case 3: dayName= "MI"
        case 4: dayName= "DO"
        case 5: dayName= "FR"
        case 6: dayName= "SA"
        case 7: dayName= "SO"
    }

    val person1On = gAlarm.members.filter[s | s.name == person+dayName].head.state

    if(person1On == ON){
        var sollMinute = (gAlarm.members.filter[s | s.name == person+day+"_M"].head.state as Number).intValue
        var sollStunde = (gAlarm.members.filter[s | s.name == person+day+"_H"].head.state as Number).intValue
        var runTime = (gAlarm.members.filter[s | s.name == person+day+"_RUN"].head.state as Number).intValue

        if(sollMinute == now.getMinuteOfHour && sollStunde == now.getHourOfDay) {
            sendCommand(person+"_AKTIV", ON)

            // do stuff

            timerPERSON1Wecker = createTimer(now.plusMinutes(runTime), [|
                // do stuff
                sendCommand(person+"_AKTIV", OFF)
                timerPERSON1Wecker = null
            ])
        }
    }
end

I also beleive this is a perfect place to use lambdas so you don’t have to copy and paste the rule above over and over for each person. I would go about it as follows:

import org.eclipse.xtext.xbase.lib.Functions
import java.util.Map

val Map<String, Timer> timers = newHashMap

val Functions$Function3 <String, Map<String, Timer>, Functions$Function0, Functions$Function0, Boolean> processAlarm = 
[person, timers, alarmStart, alarmStop |

    var dayName= "NA"
    switch now.getDayOfWeek{
        case 1: dayName = "MO"
        case 2: dayName = "DI"
        case 3: dayName= "MI"
        case 4: dayName= "DO"
        case 5: dayName= "FR"
        case 6: dayName= "SA"
        case 7: dayName= "SO"
    }

    val person1On = gAlarm.members.filter[s | s.name == person+dayName].head.state

    if(person1On == ON){
        var sollMinute = (gAlarm.members.filter[s | s.name == person+day+"_M"].head.state as Number).intValue
        var sollStunde = (gAlarm.members.filter[s | s.name == person+day+"_H"].head.state as Number).intValue
        var runTime = (gAlarm.members.filter[s | s.name == person+day+"_RUN"].head.state as Number).intValue

        if(sollMinute == now.getMinuteOfHour && sollStunde == now.getHourOfDay) {
            sendCommand(person+"_AKTIV", ON)

            // alarmStart is a passed in lambda that implements the stuff to do for that person's alarm
            alarmStart.apply()

            timers.put<person, createTimer(now.plusMinutes(runTime), [|
                // alarmStop is a passed in lambda that implements the stuff to do at the end of that person's alarm
                alarmStop.apply()
                sendCommand(person+"_AKTIV", OFF)
                timers.put(person, null)
            ])
        }
    }

    true
]

rule "Weckers"
when
    Time cron "0 0/1 * * * ?"
then

    val Functions$Function0 <Boolean> person1Start = [|
        // alarm start code
        true
    ]
    val Functions$Function0 <Boolean> person1Stop = [|
        // alarm stop code
        true
    ]

    // define functions for each person, if there is nothing to do for that person you can use [| true]
    // as illustrated with person2 below.

    processAlarm.apply("PERSON1", timers, person1Start, person1Stop)
    processAlarm.apply("PERSON2", timers, [| true], [| true]) // PERSON2 has nothing to do when the alarm starts and ends so we pass empty lambdas
    
end

Notes:

  • The above is OH 2 code. I think it will work for OH 1 as written but you may need one or more imports.
  • I just typed this in, on my phone. It likely contains some errors but it should be pretty close to right.
2 Likes

Thanks Rich, this is making the whole thing much more elegant.
Unfortunatly my OH Hardware is on the brink of failure and my first priority atm is replacing it :frowning:
But once that’s done I’ll adapt this and update the tutorial - with due credits of course :wink:
Now I’ve got to make up my mind if I take the opportunity to upgrade to OH2 or stick to OH1.8. Time for a pro and con list i suppose…

1 Like

There is no reason to stay with OH1.8 I’d say. OH2 supports all you’ve used and done till now plus some new things you might find useful. For me the main reason for switching over would be 1) the newer versions of Bindings and 2) Basic UI over Classic UI.

For your new system you should consider openHABian for an easy start :wink:

LOL I know you’re 100% pro upgrading :slight_smile: but I do have doubts - mostly concerning the Squeezebox and Astro Bindings. Squeezebox is the core of my sound and information system and from what I’ve read so far I don’t think it’ll do what i need. Same for Astro - the functionality has changed (and not for the better) quite a bit imho. But if I’m not mistaken i could stick to the latest 1.x Bindings for those. Then i like file based editing… Of course I’m aware that sooner or later I’ll have to upgrade.
As for the OS - I prefer Ubuntu Server, mainly because I’ve been using it for years.

I can’t speak to Squeezebox. But if you are concerned you can stick with the 1.x version of the binding.

The Astro 2 binding now has parity with the capabilities of the 1.9 binding. In fact it even has autodiscovery so you don’t even have to look up your lat/long if you don’t want to.

What functionality in 1.x do you think 2 lacks?

You can maintain pretty much all of your configuration in text files. The only exception I know of is zwave and that is primarily because chris hasn’t documented it yet (it is apparently pretty complicated). And even if you do use PaperUI or Habmin to configure your OH and Items and such it all gets saved to a text based JSON formatted DB. So you can source control and manually edit that just as you can your .items et al files.

Of course, if you go all manually created text based config you give up on autodiscovery which can be pretty useful and central in some binding’s cases (e.g. zwave).

OK, OK I give up! :wink:
OH2 it’ll be. New HW is due tomorrow, weather forecast for the weekend is…lousy…perfect!
Edit: I assume following this: http://docs.openhab.org/tutorials/migration.html should guide me through any troubled waters?
For Squeezebox, I think i have to stay on 1.x. I checked here: http://docs.openhab.org/addons/bindings/squeezebox/readme.html and I simply can’t find a way to use my local TTS.
Reading up on the other Bindings ( http://docs.openhab.org/addons/bindings.html ) i use (including Astro) I will give the OH2 versions a try.

Just a little update:
OH2 - up and running. Well 80% I’d say.
Allready running:

  • Persistence (MySQL)
  • Network
  • Transformation
  • Weather (1.9 using Yahoo)
  • Samsung TV (TV recognised, but items won’t trigger stuff)
  • Mail
  • Astro
  • Z-Wave (Found all Nodes, but two Battery sensors still show as “Unknown Device”…guess I’ll need to wait some wakeup cycles)

Rules seem to run OK too. All in all this went much smoother than I anticipated. There’s a few glitches here and there but nothing near a showstopper. Only real bummer left is squeezebox.

2 Likes

I have updated the original post a bit.
I made comments to distinguish between use in OH1.x and OH2.
Also I have added a “Preset” functionality. So if you are working shifts in a weekly, monthly or whatever cycle, you can define a preset for late/early wakeup hour, minute and runtime and apply it for every weekday with the press of one button. Yeah I’m such a lazy %$§&! :wink:
Thanks to @rlkoshak for advising on group concepts which greatly reduced code lines.
Once I’m more comfortable with that i will revamp the other rules to reduce complexity.

1 Like

Hello Oliver! First of all I would like to thank you for sharing this.

If you don’t mind I have one recommendation regarding the timer definition in the rules file: it should be moved on top. If it is placed in the middle of the file, then we end up with errors in the OH2.1 log, like the one below.

Configuration model ‘test.rules’ has errors, therefore ignoring it: [45,1]: missing EOF at ‘var’

Hi Geo,
thanks for pointing that out. Indeed in my rules file this actually is on top. Must have ended up in the middle when I edited the code to be generic. I have edited the post accordingly.
Regards,
-OLI

Sorry. I haven’t used functions before in OH.

I added the above, and ended up with A LOT of errors! starting with:

Multiple markers at this line

  • Incorrect number of arguments for type Function3<P1, P2, P3,
    Result>; it cannot be parameterized with arguments <String,
    Map<String, Timer>, Function0, Function0, Boolean>
  • Function0 is a raw type. References to generic type Function0
    should be parameterized

on line containing: val Functions$Function3 <String…

Do I need to do anything special when using functions?

Craig

Please post your code. It is hard to guess without the code.

Also please post the full error in the log.

I will tonight…at the ice rink right now :smile:

Here is my rules file. It is basically a copy from yours above, but with my family!
I get this error in my log:

2017-11-27 16:18:06.052 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model AlarmClock.rules'
2017-11-27 16:18:06.077 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'AlarmClock.rules' is either empty or cannot be parsed correctly!
2017-11-27 16:18:06.327 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'AlarmClock.rules' has errors, therefore ignoring it: [33,23]: mismatched input ',' expecting '}' [40,4]: mismatched input '}' expecting ']'

but, in SmartHome Designer I get A LOT of errors:

(Line#6)
Multiple markers at this line 
- Incorrect number of arguments for type Function3; it cannot be parameterized  with arguments <string, map, Function0, Function0, Boolean> 
- Function0 is a raw type. References to generic  type Function0 should be parameterized

(Line#7)
Type mismatch: cannot convert from (Object, Object, Object, Object)=>Timer to Function3<string, map, Function0, Function0, Boolean>

(Line 20)
Cannot refer to the non-final variable dayName inside a lambda expression

(Line 23, 24, 25)
The method or field day is undefined

(Line 28, 36)
Type mismatch: cannot convert from OnOffType to String

(Line 31, 35)
The method apply() is undefined for the type Object

(Line 33)
Multiple markers at this line 
- mismatched input ',' expecting '}' 
- The method or field put is undefined for the type Object

(Line 37)
The method put(Object, Object) is undefined for the type Object

(Line 40)
mismatched input '}' expecting ']'

Whew! I am sure it’s something silly, but my lack of experience with openHab functions is glaringly obvious!

This starts at Line 1:

import org.eclipse.xtext.xbase.lib.Functions
import java.util.Map

val Map<String, Timer> timers = newHashMap   

val Functions$Function3 <String, Map<String, Timer>, Functions$Function0, Functions$Function0, Boolean> processAlarm = 
	[person, timers, alarmStart, alarmStop |

		 var dayName= "NA"
		 switch now.getDayOfWeek{
			  case 1: dayName = "MO"
			  case 2: dayName = "DI"
			  case 3: dayName= "MI"
			  case 4: dayName= "DO"
			  case 5: dayName= "FR"
			  case 6: dayName= "SA"
			  case 7: dayName= "SO"
		 }

		 val person1On = gAlarm.members.filter[s | s.name == person+dayName].head.state

		 if(person1On == ON){
			  var sollMinute = (gAlarm.members.filter[s | s.name == person+day+"_M"].head.state as Number).intValue
			  var sollHour = (gAlarm.members.filter[s | s.name == person+day+"_H"].head.state as Number).intValue
			  var runTime = (gAlarm.members.filter[s | s.name == person+day+"_RUN"].head.state as Number).intValue

			  if(sollMinute == now.getMinuteOfHour && sollHour == now.getHourOfDay) {
					sendCommand(person+"_Active", ON)

					// alarmStart is a passed in lambda that implements the stuff to do for that person's alarm
					alarmStart.apply()

					timers.put<person, createTimer(now.plusMinutes(runTime), [|
						 // alarmStop is a passed in lambda that implements the stuff to do at the end of that person's alarm
						 alarmStop.apply()
						 sendCommand(person+"_Active", OFF)
						 timers.put(person, null)
					])
			  }
		 }

		 true
	]

//===================================================================================
rule "Alarms"
when
    Time cron "0 0/1 * * * ?"
then

    val Functions$Function0 <Boolean> CraigAlarmStart = [|
			sendCommand(Light_US_MasterRoom_CraigsLamp, ON)
        true
    ]
    val Functions$Function0 <Boolean> CraigAlarmStop = [|
			sendCommand(Light_US_MasterRoom_CraigsLamp, OFF)
        true
    ]

    // define functions for each person, if there is nothing to do for that person you can use [| true]
    // as illustrated with person2 below.

    processAlarm.apply("Craig", timers, CraigAlarmStart, CraigAlarmStop)
    processAlarm.apply("Ellen", timers, [| true], [| true]) // PERSON2 has nothing to do when the alarm starts and ends so we pass empty lambdas
    processAlarm.apply("Nick", timers, [| true], [| true]) // PERSON2 has nothing to do when the alarm starts and ends so we pass empty lambdas
    processAlarm.apply("Marcus", timers, [| true], [| true]) // PERSON2 has nothing to do when the alarm starts and ends so we pass empty lambdas
    
end

What version of Designer are you using?

val Functions$Function3 <String, Map<String, Timer>, Functions$Function0, Functions$Function0, Boolean>

There are four arguments plus the return type in the < >. You need to change it to Functions$Function4

sendCommand(person+"_Active", ON)

change to

sendCommand(person+"_Active", "ON")

It’s not a problem but this will make the error go away.

					timers.put<person, createTimer(now.plusMinutes(runTime), [|
						 // alarmStop is a passed in lambda that implements the stuff to do at the end of that person's alarm
						 alarmStop.apply()
						 sendCommand(person+"_Active", OFF)
						 timers.put(person, null)
					])

should be

					timers.put(person, createTimer(now.plusMinutes(runTime), [|
						 // alarmStop is a passed in lambda that implements the stuff to do at the end of that person's alarm
						 alarmStop.apply()
						 sendCommand(person+"_Active", "OFF")
						 timers.put(person, null)
					]))

As a general rule, when the entire lambda is marked as an error it is because of a mismatched or missing (), {}, or [].

Those changes made all the errors except for those related to the fact that I don’t have the items defined go away in ESH Designer 0.8.

Note that ESH Designer is about to become deprecated in favor of the VSCODE plugin which, now that I’ve been using it a bit, is way nicer than ESH Designer. And soon it will have the same syntax checking that ESH Designer has, only better because it will be complete.

Thank you kind sir!
I will make those changes when I get home. I am using version 0.8 which I find is good but it seems to have memory issues. I find it gets slower and slower as I use it. I find this on both my Linux and windows machines. I would like to try the VSCode when it’s ready.

Also many thanks to Oliver for this thread. It’ll save me a lot of time coding which my wife appreciates :wink:

Craig

Hi Craig,

glad you like it, and sorry for not answering earlier. But real world stuff…you know how it is. Thanks Rich for helping out, you’re the best :slight_smile:
Regards,
-OLI

It is pretty usable now and it is so much nicer to use. It adds in all the stuff I missed that ESHD lacked, like a terminal where I can tail OH’s log as I edit. And I can install other plugins and do my Ansible and Python develop all in the same tool now. I’d say don’t wait. If you run into a thorny syntax problem you can always bring up ESHD again to debug those parts until the language server stuff gets finished.

1 Like

OK. Getting closer!

I get as a validation error:

Function0 is a raw type. References to generic type Function0 should be parameterized

on the line:

val Functions$Function4 <String, Map<String, Timer>, Functions$Function0, Functions$Function0, Boolean>

and

Cannot refer to the non-final variable dayName inside a lambda expression

as an error on:

val person1On = gAlarm.members.filter[s | s.name == person+dayName].head.state

I fixed this one by declaring dayName outside Function4

However, still no love… in my test I ended up with:

2017-12-02 10:19:57.723 [DEBUG] [.eclipse.jetty.server.HttpConnection] - org.eclipse.jetty.server.HttpConnection$SendCallback@643313[PROCESSING][i=null,cb=Blocker@13da6c5{null}] generate: DONE (null,[p=150,l=150,c=32768,r=0],false)@COMMITTED
2017-12-02 10:20:00.003 [DEBUG] [rg.quartz.core.QuartzSchedulerThread] - batch acquisition of 1 triggers
2017-12-02 10:20:00.003 [DEBUG] [org.quartz.core.JobRunShell         ] - Calling execute on job DEFAULT.AlarmClock.rules#Alarms#0 0/1 * * * ?
2017-12-02 10:20:00.052 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule Alarms: Cannot assign a value in null context.
2017-12-02 10:20:00.166 [DEBUG] [rg.quartz.core.QuartzSchedulerThread] - batch acquisition of 0 triggers
2017-12-02 10:20:00.166 [DEBUG] [org.quartz.core.JobRunShell         ] - Calling execute on job MapDB_SchedulerGroup.Commit_Transaction
2017-12-02 10:20:00.195 [DEBUG] [rg.quartz.core.QuartzSchedulerThread] - batch acquisition of 1 triggers
2017-12-02 10:20:00.929 [DEBUG] [org.eclipse.jetty.server.HttpChannel] - sendResponse info=null content=DirectByteBuffer@6a3773[p=0,l=177,c=32768,r=177]={<<<event: message\nda...usInfoEvent"}\n\n>>>\\":\\"49.2611283,-...ptions":[]},"ty} complete=false committing=false callback=Blocker@13da6c5{null}

in my log, and nothing triggered.

Cannot assign a value in null context.???