'Semi-smart' alarm clock

In the light of toning down on smartphone usage, I would like to switch back to a dedicated alarm clock on the bedside table. But of course, there are criteria:

  1. I want to be able to configure the alarms via all sorts of schedules, and I want openHAB to have a possible say in this. (If my calendar states I’m off from work, I don’t want to be woken at 6h00…).
  2. I want to be able to read the time.
  3. The screen needs to only spread light when I want it to (the room needs to be dark, obviously).
  4. It needs to look reasonably nice (e.g. not an naked ESP32 with wires exposed).

Of course, googling this is a disaster, as only “conventional” or “too smart” alarm clocks cross my path. (Where is the time that Google searches yielded results, instead of online shop suggestions…?)

If anyone has a suggestion, please share. :slight_smile:

1 Like

Nice topic, thanks for this! It’s really a niche “problem” to solve!

Not exactly what you want, but this is my current setup: I use an old Android 7 smartphone with “Sleep as Android” and Tasker. Tasker reads from a calendar if a specific alarm needs to be activated or not and controls Sleep as Android with Intents.

The smartphone is rooted and I use CF.lumen to make its screen dark red. And I only use it for this purpose, nothing else.

If you’re interested, I can write the Tasker rules here :blush:

openHAB is not directly involved, but Tasker also triggers a virtual switch in the morning and then some lights, heating, radio etc. are activated.

If you only want a display that has the possibility to beep at specific times, an Ulanzi TC001 might be an option as well. But it doesn’t have a speaker, just a “beeper” which can play monophonic sounds. If you flash Awtrix, you can (= have to) control it from openHAB.
A LaMetric Time is much better imho, but might be way too overengineered and expensive for your use case :grin:

Iirc it’s possible to build something on your own and flash Awtrix on it as well.

Hi, I think the Ulanzi TC001 is good..

with the binding you can do much more than just an alarm clock

Greets.

I use an Echo Show. You can turn off the screen and change/cancel next alarms with voice commands. I am not sure if a changed alarm time could be sent to OpenHAB to make the system aware.

Same here. Echo Show 5.
Automatic display dimming. Lowest setting does not disturb sleep.
Never need to find a physical button again.
Is connected to openHAB but never in use. All done through voice commands.

Ha, this looks fun! I’ll do some research about snooze buttons or possible vibration instead of sound.

Mmm, voice commands to order snoozing seems counter-intuitive. Especially for the misses…

But probably a big touch screen button could be configured?

Do you mean from openHAB? Which ‘system’ do you mean?

The docs for the Amazon Echo Control add-on has a Channel for next alarm, reminder and timer.

I am going to have to play with the next alarm Channel. I have my bedside lamp coming on every weekday at 6am. If I change my alarm on the echo show, I would like the lamp time to be the same. Still by using voice commands and not checking a calendar like OP wanted.

Which binding do you mean exactly, by the way?

Awtrix 3 Binding.

I did some reading up, and scrolling YouTube videos. I’m sure it can do much more than just an alarm clock, but I haven’t found any reference of it actually being used as alarm clock…

I guess it only makes very basic sounds (of which I unfortunately found 0 examples on YouTube - too bad it gives so many irrelevant search results). So that’s maybe not a very relaxing way to be woken? I also assume it doesn’t vibrate? (For some reason I find that a nicer sound to wake up to than some tune I start hating after a few days/weeks.)

Could you give more information on those points?

This sounds like exactly what I need. But I don’t have and old smartphone lying around… (And maybe having one plugged in 24/7 worries me a bit…)

Indeed :sweat_smile:

I assume you use some sort of native Alexa app to configure the alarms?

@rlkoshak, is there even a way to save a ‘schedule’ in openHAB? I don’t think I’ve come across something like it yet…

No, in 95% just voice commands. It is much more convenient than using an app or openHAB.
But through the Amazon Echo Control binding I can see the alarm times and all other available channels. I am pretty sure it can also be set through openHAB, but that was never an option for me. I guess it could also be done through the iCalendar binding.

Example: in the evening: “Alexa, set alarm to xx:xx o’clock” or if it is already set: “Alexa, tell me the next alarm time”.
In the morning: “Alexa, stop” when the alarm sounds, after that you can get recent news or the weather forecast automatically. Of course the alarm also works without internet connection.
For me it is one of the cheapest (when they are on sale) smart alarm clocks available.

Yes, there are just those annoying beep sounds..

But for an alarm clock a ‘beep beep beep’ is enough :grinning_face_with_smiling_eyes:

no vibration.

  • Time is Item (ignore date) rule triggers
  • cron triggers
  • iCal binding
  • There is a Timeline Picker tutorial out there but I don’t know if it works with OH 5 or not. I think it should but :person_shrugging: Timeline picker to setup heating, light and so on - #934 by milo
  • Time Based State Machine [4.0.0.0;5.9.9.9]
  • One could put the scheduled future alarm times into the in memory persistence; when the future time comes the alarm Item will be updated with the value stored in memory
  • any of the above with Ephemeris conditions (e.g. don’t alarm on a holiday)

Timeline picker runs with oh5.x but you need to disable the thing otherwise you never get into running mode of openhab.

1 Like

I’ve decided to go with the Ulanzi TC001. The Echo Show 5 has a more “boring” look, and it also looks like (what it more or less is, namely) a smartphone with a speaker. And I want to ban the smartphone from the bedroom.

Unfortunately, the Ulanzi TC001 won’t be delivered before Thursday. That’s unfortunate, since I’ve got time before Thursday, but less so as from Thursday. But it is what it is: I’ll try to prepare what I can.

What I hope to achieve is that the Ulanzi will display the current time 24/7 (so I hope I can disable all the other Ulanzi/Awtrix “apps”), and an alarm clock icon or something like it, if my DateTime Item wekker_eerstvolgende isn’t NULL. If I press one of the Ulanzi’s buttons, the value of said Item appears, showing me when my next alarm will go off. If it goes off, the other button would let me “snooze” for x minutes. Another button press would “dismiss” the alarm clock, and set wekker_eerstvolgende to NULL. (I’ve got the impresson only two buttons of the Ulanzi can be set for some use, so I might have to rethink something…)

Since I need the time of the next alarm clock, I don’t think a cron based rule would work, so I created a bunch of DateTime Items, a few switches and a (rather convoluted, sadly) rule (see below). Some context for the rule: I’ve got my “days off” marked in my Google Calendar, which I extract with the iCalendar binding. If such a period is ongoing, the switch wekker_schoolwekkers_actief is OFF; the first day of the next period of “days off” is DateTime Item begin_volgende_schoolvakantie; the last day of the currently ongoing period of “days off” is DateTime Item einde_huidige_schoolvakantie. Script melding_gsm_DBE contains actions.NotificationAction.sendNotification(). Switch Item wekker_vakantiewekkers_actief is something I’ll switch on or off manually (but is switched off in the rule below when my “time off” will be over tomorrow).

I’ve created a DateTime Item for every weekday in the “working period” (e.g. wekker_school_maandag), one for every weekday in the “days off period” (e.g. wekker_vakantie_maandag), and a few for “one-off events” (e.g. wekker_eenmalig_01). I’ve also got a Group Item wekker_openhab, which contains all my DateTime “alarm clock” Items:

version: 1
items:
  wekker_openhab:
    type: Group
    group:
      type: DateTime
    label: Wekkers in openHAB

This is the rule, running daily at 18h00:

var controleperiode = 18; // in uren


var weekdagen = ["maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag", "zondag"];
var allewekkers = items["wekker_openhab"].members;
var wekker_schoolwekkers_actief = items["wekker_schoolwekkers_actief"];
var wekker_vakantiewekkers_actief = items["wekker_vakantiewekkers_actief"];
var begin_volgende_schoolvakantie = items["begin_volgende_schoolvakantie"];
var einde_huidige_schoolvakantie = items["einde_huidige_schoolvakantie"];
var wekkersoorten = {
  "school": "wekker_school_",
  "eenmalig": "wekker_eenmalig_",
  "vakantie": "wekker_vakantie_"
};


function wekkersSorteren(a, b) {
  if (time.toZDT(a).isBefore(time.toZDT(b))) {
    return -1;
  } else {
    return 1;
  }
}


function wekkerSelecteren(wekkers) {
  if (wekkers.length > 1) {
    var melding = "Er is voor morgen meer dan één wekker ingesteld; enkel de vroegste wordt geactiveerd."
    console.log(melding);
    rules.runRule("melding_gsm_DBE", { 'melding': melding });
    wekkers.sort(wekkersSorteren);
  }
  return wekkers[0];
}


function schoolWekkerActief() {
  if
      (
        (
          wekker_schoolwekkers_actief.state == "ON"
          &&
          (
            (
              begin_volgende_schoolvakantie.state != "NULL"
              &&
              !time.toZDT(begin_volgende_schoolvakantie).toLocalDate().equals(time.toZDT().plusDays(1).toLocalDate())
            )
            ||
            begin_volgende_schoolvakantie.state == "NULL"
          )
        )
        ||
        (einde_huidige_schoolvakantie.state != "NULL" && time.toZDT(einde_huidige_schoolvakantie).toLocalDate().equals(time.toZDT().toLocalDate()))
      ) {
    wekker_vakantiewekkers_actief.sendCommandIfDifferent("OFF");
    return true;
  } else {
    return false;
  }
}


function main() {
  var nu = time.toZDT();
  var schoolwekkeractief = schoolWekkerActief();
  var eenderwelkewekkermorgen = false;
  var wekkersmorgen = {
    schoolwekkermorgen: false,
    schoolwekkers: [],
    vakantiewekkermorgen: false,
    vakantiewekkers: [],
    eenmaligewekkermorgen: false,
    eenmaligewekkers: []
  };
  for (var x = 0; x < allewekkers.length; x++) {
    var dezewekker = allewekkers[x];
    if (dezewekker.state != "NULL") {
      if (dezewekker.name.includes(weekdagen[nu.dayOfWeek().value()-2]) && (dezewekker.name.includes(wekkersoorten["school"]) || dezewekker.name.includes(wekkersoorten["vakantie"]))) {
        dezewekker.sendCommand(time.toZDT(dezewekker).plusDays(7));
      } else if (time.toZDT(dezewekker).isBefore(nu.plusHours(controleperiode))) {
        eenderwelkewekkermorgen = true;
        if (schoolwekkeractief && dezewekker.name.includes(wekkersoorten["school"])) {
          wekkersmorgen["schoolwekkermorgen"] = true;
          wekkersmorgen["schoolwekkers"].push(dezewekker);
        } else if (wekker_vakantiewekkers_actief.state == "ON" && dezewekker.name.includes(wekkersoorten["vakantie"])) {
          eenderwelkewekkermorgen = true;
          wekkersmorgen["vakantiewekkermorgen"] = true;
          wekkersmorgen["vakantiewekkers"].push(dezewekker);
        } else if (dezewekker.name.includes(wekkersoorten["eenmalig"])) {
          eenderwelkewekkermorgen = true;
          wekkersmorgen["eenmaligewekkermorgen"] = true;
          wekkersmorgen["eenmaligewekkers"].push(dezewekker);
        }
      }
    }
  }
  if (eenderwelkewekkermorgen) {
    if (wekkersmorgen["eenmaligewekkermorgen"]) {
      var uiteindelijkewekker = wekkerSelecteren(wekkersmorgen["eenmaligewekkers"]);
      items["wekker_eerstvolgende"].sendCommand(uiteindelijkewekker.state);
      if (wekkersmorgen["schoolwekkermorgen"]) {
        var melding = "Er is voor morgen een eenmalige wekker ingesteld; de normale schoolwekker wordt dus genegeerd."
        console.log(melding);
        rules.runRule("melding_gsm_DBE", { 'melding': melding });
      }
    } else if (wekkersmorgen["schoolwekkermorgen"]) {
      var uiteindelijkewekker = wekkerSelecteren(wekkersmorgen["schoolwekkers"]);
      items["wekker_eerstvolgende"].sendCommand(uiteindelijkewekker.state);
    } else if (wekkersmorgen["vakantiewekkermorgen"]) {
      var uiteindelijkewekker = wekkerSelecteren(wekkersmorgen["eenmaligewekkers"]);
      items["wekker_eerstvolgende"].sendCommand(uiteindelijkewekker.state);
    }
  }
}


main();

Now onto finding out how these Ulanzi/Awtrix “apps” work…

I’ve also started to explore Tasker, so that another Ulanzi/Awtrix “app” can make some noise or something when one of my starred contacts tries to call me. Additionally, it would be great if I could also control the alarm on my Android phone, and snooze it when I press the “snooze” button on the Ulanzi.

In order to make this all manageable in the UI, I’d like to make a page with collapsable “sections”, so each alarm clock has its own time-picker (or how is it called), and a button to set it to NULL.

I’ll keep you - the reader, or the non-reading world - posted.

Have you looked at Ephemeris? It might be a way to make the logic quite a bit simpler.

From a maintainability perspective this code is a little all over the place (hints as AI generation). Sometimes you use Booleans, other times you use 1, and -1. Some of the logic isn’t even here but in another rule. In the long run it will be challenging to update this rule when needed and keep it running. It is probably worth your time to redo it with an eye for simplification and DRY (do not repeat yourself).

You might also get some simplification by putting the logic that determines whether and when the alarm needs to sound into the rule’s condition and let the script action just handle the “what to do”.

There are some little things you can do too without totally restructuring your rule to ake it easier to read.

  • wekker_schoolwekkers_actief.state == "ON"wekker_schoolwekkers_actief.boolState

  • begin_volgende_schoolvakantie.state != "NULL"!begin_volgende_schoolvakantie.isUninitialized (this also catches the case where the state is UNDEF

  • time.toZDT().plusDays(1)time.toZDT('P1D') (toZDT() supports ISO8601 duration strings

  • If you work regular days of the week and/or school is regular days of the week (e.g. mon-fri) you can configure those using Ephemeris and replace the big complicated if statement in schoolWekkerActief where you compare the dates to !actions.Ephemeris.isWeekend(). You can also connect this to Items using the Ephemeris binding so you could just check an Item’s state. Ephemeris lets you define you own daysets so you could use actions.Ephemeris.isInDayset('school').

  • for (var x = 0; x < allewekkers.length; x++) {allewekkers.forEach(dezewekker () => (it’s often more effecient and definitely easier to read when iterating over an Array usign forEach. You can even eliminte that check for “NULL” inside the loop using a filter: allewekkers.filter( (wekker) => !wekkker.isUninitialized).forEach(dezewekker () =>. That will filter out those Items that are NULL before you loop over them. In fact the whole loops and if statements could be simplified or at least made easier for humans to read with filters:

var allewekkers = items.wekker_openhab.filter(dezewekker => !dezewekker.isUninitialized);

...

allewekkers.filter(dezewekker => dezewekker.name.includes(weekdagen[nu.dayOfWeek().value()-2]) 
                                 && dezewekker.name.includes(wekkersoorten["school"] 
                                 || dezewekker.name.includes(wekkersoorten["vakantie"]))
           .forEach(dezewekker => dezewekker.postUpdate(time.toZDT('P7D'));
          
  • .name.includes(weekdagen[nu.dayOfWeek().value()-2]) this seems needlessly complicated bu tI don’t understand what it does so can’t offer concrete suggestions. I would just include what ever dayOfWeek() returns in the names of the Items instead of this complicated lookup table.

To offer much more in terms of simplification would require I know more about what exactly this code is supposed to do and the Items involved. It seems like a lot of the complications can be handled just by adding some more Groups instead of needing to keep filtering the one big list of alarms, split them up in the first place.

Just as an idea how to do it differently.
The way we do it since about more than 15 years (and it is accepted and probably even appreciated by my wife):
We use the Sonos music system as a source of the alarm (and the Sonos speaker wakes us up with the BBC news. You may want to go for something ore cheerful these days).
The Sonos alarm triggers a rule that switches on the bedroom light at lowest level for 20 minutes.
When the Sonos alarm stops, another rule switches the lights to 60 % and turns on the coffee machine so it preheats while we are in the bathroom.
Late we have a capuccino (I’m doing that the traditional way, no automation whatsoever, just getting the best out of coffee beans and frothed milk).
I would not recommend Sonos as a music system any longer, but the whole thing works pretty well every morning during the week and we don’t want to miss it..
I actually get it in my ear each time it fails due to some upgrade (mainly Sonos not responding).

Just my own thoughts and shortcomings :wink: Thanks for the hints, I’ll get to work with them.

Not all weekdays require the same alarm clock, so it’s not that simple. But during the time off, there’s no need for different alarm clocks, in hindsight :slight_smile: