Beginner's guide to an openHABian reboot/shutdown switch

Tags: #<Tag:0x00007f2fc2fa8ae0> #<Tag:0x00007f2fc2fa8950>

As I get deeper into openHAB, I’m finding that I have to reboot or shutdown my Raspberry Pi reasonably often. It seemed like a good idea to set up a switch on my sitemap for that purpose, but was not nearly as easy as I thought it would be. I learned almost everything I needed to know for this task from the openHAB community, but it was a challenge interpreting expert-level discussions that quickly went over my head.

So, this tutorial is written for folks like me who are at an introductory level. I’m going to go into a fair amount of detail, covering some of the basics that are not obvious if you’re just getting started. And even then, I’ll probably miss some of the little details that I already take for granted.

FYI, in my attempts to explain stuff at a Grade 5 reading level, I’m almost certainly going to over-simplify in a way that will drive the veterans crazy. Sorry in advance!

Assumptions

This tutorial assumes that you’ve installed openHAB 2.4 using the openHABian installation guide, and are comfortable installing bindings via Paper UI and creating items, rules, and sitemaps via configuration files.

Why openHAB 2.4 via the installation guide? Because that’s what I did. If you install openHAB manually on an RPi or on another type of machine, your configuration might be different and some of these instructions might not work.

1. Grant permission for openHAB to execute Linux commands

Same as any other operating system, a Linux system has users. Some users can run important commands (like rebooting or shutting down), and others are prevented from doing so.

When openHABian was installed, it created two important users:

  • “openhabian” is a very powerful user that has extensive access to the OS. It was created with a default password of “openhabian”, which you might have already changed via the openHABian Configuration Tool.
  • “openhab” is a user that has no password and limited permissions. When the openHAB program is running, it communicates with Linux as the “openhab” user. So in order to run the reboot and shutdown commands from openHAB, we have to grant permission to this user.

Basically, we’re going to log into the system as the very powerful “openhabian” user and grant reboot and shutdown permissions to the very limited “openhab” user.

Access your Raspberry Pi

You’ll need an SSH client program, which you’ll use to access your RPi over your home network using a “Secure Shell” (SSH). The client enables us to type Linux commands that will be run on the RPi. I use PuTTY on my Windows 10 laptop, because that’s what this article told me to do.

Connect to your Raspberry Pi using your SSH client:

  • Host name: “openhabianpi” or your RPi’s IP address
  • Port: 22
  • Username: openhabian
  • Password: If you didn’t change this using the openHABian Configuration Tool, then it’s “openhabian” (if it is, you should really change your password).

Make openhab a super user

We’re going to run some “sudo” commands in Linux. sudo stands for “super user do”. When you start a command with it, you’re telling Linux to run the command with administrator-level (e.g. super user) permissions. Because of this, you’ll have to re-enter your password a few times to confirm the commands we’ll type in the following sections.

Start by telling Linux to add “openhab” to the group of users who can run sudo commands:

sudo adduser openhab sudo

Specify commands that openhab can run

Now we have to tell Linux which commands the “openhab” user is allowed to run. We’ll use the “visudo” command to create a new text file and open a text editor:

sudo visudo -f  /etc/sudoers.d/yourfilenamehere

When the text editor opens, add the following lines to the new (and blank) file:

# Allow openhab user to execute reboot and poweroff commands
openhab   ALL=(ALL) NOPASSWD: /sbin/reboot, /sbin/poweroff

The first line is a comment for the purposes of describing what we’re doing. You can change it to whatever you like, so long as it starts with #.

The second line specifies that the “openhab” user can run the Linux reboot and poweroff commands without entering a password. I don’t entirely understand what the “ALL” part is for, so let’s just remember that this is a beginner’s guide and leave it alone. EDIT: (@rlkoshak explains below)

If you want to run other system commands from openHAB, just tack them on to the end of the line. It’s also possible to grant permission to run any command, but that’s a bad idea. It’s best to limit users to only the commands they need.

There’s a list of key commands at the bottom of the text editor. To save and exit, we’ll use the ^X command, which translates to CTRL+X on a Windows keyboard. Visudo will prompt you to save your changes:

Save Modified Buffer? Y for Yes
File Name to Write: /etc/sudoers.d/yourfilenamehere.tmp

I was confused that visudo added “.tmp” to the file name, and initially thought it was saving a new file. However, when I accepted the file name as is, the changes were updated in my original file. So, you can just press Enter on your keyboard to accept the file name. (EDIT: @Udo_Hartmann confirms below that visudo saves a copy, then overwrites the original).

If you want to confirm that your changes were saved, enter the “sudo visudo…” command above to re-open the text editor. You can do this quickly by using your keyboard’s Up/Down arrow keys to scroll through recent commands (you can even see commands from previous SSH sessions).

Test the new permissions

Test the permissions by running the reboot command. First, we’ll use a sudo command to change to the “openhab” user. Then, we’ll reboot the RPi:

sudo -u openhab /bin/bash
sudo reboot

If all goes as planned, your SSH client will kick you out because your RPi is rebooting, and we are done with this step.

2. Create an item

Now we’ll create a virtual switch in an item file. Its sole purpose will be to trigger reboots and shutdowns via the sitemap, so it doesn’t need to be connected to a thing.

String Flag_System "System" <switch>

Call the item whatever you want, but make sure it’s a string so that it can have multiple states (not just On and Off). You could also make individual switches (one for reboot and one for shutdown), but I prefer this approach.

3. Add the item to your sitemap

I’ve added the switch to my sitemap as follows:

Selection item=Flag_System label="System" mappings=[NULL="Active", Reboot="Reboot", PowerOff="Shut down"]

With a string-type item and a selector, I have to purposefully select Reboot or Shut down from a popup menu to confirm the command, so it’s very difficult for me to accidentally trigger a reboot.

4. Set up rules

Reboot rule

EDIT: I’ve made some improvements to my reboot rule to incorporate feedback from @rlkoshak and @marcel_erkel.

rule "Reboot openHAB"
when
    Item Flag_System received command "Reboot"
then
   	logInfo("Flag_System", "Rebooting openHAB")
    executeCommandLine("sudo@@reboot", 60000)
end

When I change the virtual switch in my sitemap to “Reboot”, two things happen:

1. The reboot is recorded in the system log.

If you don’t want the “Rebooting openHAB” message in your log, you can delete this line.

2. A reboot is triggered

“sudo reboot” is run using the Exec action “executeCommandLine()”. This is similar to using sendCommand() when you want to turn a light switch on or off, but in this case we’re running a system command.

If you want to use openHAB to trigger other commands, just replace “sudo@@reboot” with “sudo@@etc@@etc”, using @@ wherever you want spaces. Don’t forget to grant permissions to the “openhab” user with visudo.

The 60000 is a 60-second time out for openHAB to receive a response (thanks to @Udo_Hartmann for confirming this and pointing me to the Exec action documentation). I read somehere that executeCommandLine might be more reliable with a response time specified. At @rlkoshak’s suggestion, I tried removing it and my system rebooted just fine. So I’m not using it, but left it here for educational purposes.

Shutdown rule

This is basically the same thing as the reboot rule, but the system stays off. I use this when I want to unplug my RPi.

rule "Shut down openHAB"
when
    Item Flag_System received command "PowerOff"
then
	logInfo("Flag_System", "Shutting down openHAB")
    executeCommandLine("sudo@@poweroff")
end

Note that I left the 60-second response time off of this rule.

Side note: persistence

I have mapdb persistence set up, so whenever openHAB starts it returns items to their last reported state. My original persistence strategy persisted every item in my system, but I wanted to exclude Flag_System so that it will always return to a NULL state. Otherwise, Flag_System would go back to “Reboot” every time the system starts.

The solution was to create a group and add only the items that I want to persist to it. Then, I changed my persistence strategy to only persist items in that group:

// mapdb persistence
Strategies
{
    default = everyChange
}
Items
{
    Group_Persist* : strategy = everyChange, restoreOnStartup
}

I encourage you to set up persistence if you haven’t done so already.

6. Test your rules

Once your rules are in place and saved, testing them is as simple as going to your sitemap and selecting Reboot or Shut down. I’m 93% confident that they’ll work.

Questions?

I’m really enjoying openHAB, and I’m grateful to all of the people who have contributed to this community, whether you’re asking basic questions or helping others solve tough problems. For this particular challenge, I learned a lot from these discussions:

I hope this tutorial helps other beginners to better understand your openHAB systems and gain confidence in your abilities. If you have any feedback or suggestions, I’d love to hear them!

9 Likes

Nice HowTo :slight_smile:

Easy :slight_smile: visudo has to store the file, while storing check that there are no issues, then overwrite original file.

Wrong place. :slight_smile: it’s not the exec binding, it’s the exec action:

1 Like

Ah, I thought that might be the case with visudo, but declined to speculate.

So, I guess I don’t actually need the exec binding if I’m just running an action, since it’s a core feature? If so, I’ll remove that from the tutorial. That explains why I couldn’t find anything, since I kept searching for “exec binding”.

Thanks!

EDIT: Yep, no need for the Exec binding, so I’ve deleted that step.

Great post. And thank you for not giving the openhab user sudo on ALL commands! Also, thank you for using visudo. I’ve helped more than one user who didn’t and hosed their system as a result.

In English the line reads, “Give”:

openhab ALL (ALL) NOPASSWD /sbin/reboot, /sbin/poweroff
the user openhab on any host as any target user without a password permission to run these two commands.

If you wanted to limit the permission to just your OH host you would put openhabianpi for the first “ALL”. This is more useful in an environment with lots of computers and shared config files.

The second (ALL) can be used to limit what users this user is allowed to run commands as. For example, sudo has a -u option where you can provide a username to run something as.

sudo -u openhab ls

If you are running as user openhabian, the above command will execute the ls command as the openhab user.

You could also use (root) in this context.

openhab ALL=(root) NOPASSWD: /sbin.reboot, /sbin/poweroff

This would prevent OH from trying to run either of these commands as some other user. Because these two commands can only be run by root, and only these two commands can be run by openhab limiting the users to root doesn’t really add much. Where it is more useful when you want to give one “regular” user permission to run commands as some other “regular” user instead of root.

Given some of the startup timing problems that exist in OH 2, I recommend this approach even with persistence and restoreOnStartup, perhaps when a small Thread::sleep to make sure that MapDB has saved the new value.

Furthermore, I recommend changing to received command instead of changed. This will also help avoid the problem as the Rule will only trigger when you explicitly command the Item. Then restoreOnStartup can never trigger the Rule, or any other update for that matter. You have to command it (from a Rule or the Sitemap).

Have you tried it with no timeout? You don’t care about the return from the commands so your Rule doesn’t really need to wait around for it to complete, unless forcing it to wait around helps avoid a problem like the reboot command being killed when OH dies as a result of the reboot command.

1 Like

I would not use persistence at all for the Flag_System item. Instead I would make use of the System started trigger:

rule "System startup"
when
    System started
then
    Flag_System.sendCommand("Active")
end

Last night, I thought about exempting Flag_System in my persistence strategy so that it would always return to NULL and the Active setting wouldn’t be necessary. I haven’t done much studying on persistence strategies…so I guess now is a good time.

I’m currently just persisting everything on startup:

Strategies 
{
    default = everyChange 
}
Items 
{
    * : strategy = everyChange, restoreOnStartup
}

I’m guessing that I need to remove the global * and specify a group of items that should persist, so that Flag_System is excluded. If so, is this all I need to have in my mapdb.persist file?

Strategies 
{
    default = everyChange, restoreOnStartup
}
Items
{
    Group_Persisted_Items*
}

And of course, every item I want to persist would have to be added to Group_Persisted_Items.

Thanks for the details, Rich! Your comments about permissions and visudo are a large part of why I felt this would be a useful topic for my first attempt at a tutorial.

I’ll definitely modify my rules to use “received command” instead of “changed”.

As for the timeout, I didn’t try going without it. I saw another post where you mentioned needing response time, so I decided it was best to leave it alone since it seems more reliable in other uses.

Hi . I have a question . how would I implement this to restart a remote Pi. what would the best way be too do that. via the EXEC action or via say MQTT and what if the Pi loses wifi. will I use a lwt topic from my openhab to restart the pi.
I am from South Africa and we are battling some days with stable power supply in ow towns. and the PI in question runs my remote db that in turn runs my enviro monitoring. and uses node red to communicate between mqtt and openhab.

You would probably want to use ssh to log into the RPi to issue the commands. But that obviously won’t work if the RPi had fallen off the network.

The lwt topic is independent from the client by design. It is actually the broker that published the message to the lwt topic when the client goes offline.

You could also use some sort of script or service on the RPi that issues the command based on an MQTT message, but again, if the RPi had fallen off the network it won’t receive any MQTT messages.

So all of this is going to depend on what happens when you lose power. Is the RPi still running on an ups? If not there is no command to issue, the RPi is already off.

Do you still have network? If not there is nothing you can do from OH. Any restart will have to bed immigrated on the RPi itself.

My whole network and the server and my remote db runs of a backup system (ups) so network is there so even then it all runs. But some times i need to reboot the pi in the db. And its in the roof so jip looking for a easy way out

Regards
Allen

Do you need the reboot to happen automatically via a rule, or would you trigger it manually? Before I set this up, I just used an Android app called Raspcontroller to reboot my RPi. That might be the easiest solution.

If you really think you need to restart it (why at all? Pis restart when power is restored. And any proper SW such as the MQTT client should restore connectivity with the server when that becomes available again) I’d suggest to invert the mechanism:
Set up a cron job or similar on your remote Pi to have it check back with your local system at regular intervals (MQTT broker or API call or whatever to use as an indicator for a working connection) and if it can NOT reach your local system then instruct it to reboot itself.

I second that, exactly what I would do. a sort of watchdog on the pi that should restart itself…

Nice tutorial! I have implemented this idea trough accessing gpio and switching gpio to reboot and shutdown using python script run at system start-up/boot.
My concern for this tutorial is that during the execution, openhab, grafana, influxdb, etc. cannot be stop gracefully during reboot or shutdown. Especial for Openhab.service since you cannot stop openhab then initiate shutdown.

@rlkoshak Based on this discussion, should I update this tutorial to use sudo shutdown -r instead of sudo reboot?

If so, am I correct that I’ll need to modify sudoers.d to:

# Allow openhab user to execute shutdown and poweroff commands
openhab   ALL=(ALL) NOPASSWD: /sbin/shutdown, /sbin/poweroff

I also noted that Buster now uses systemctl reboot. So in that case, is it:

# Allow openhab user to execute systemctl command
openhab   ALL=(ALL) NOPASSWD: /sbin/systemctl

And then for the commands:

sudo systemctl reboot
sudo systemctl poweroff

Or is sudo no longer necessary for Buster due to systemctl being available to all users?

Probably. Though there is some doubt as to whether that is an issue in Buster or not. But since it is an issue in older raspbians the tutorial could use an update. Apparently on Buster, both reboot and shutdow -r actually alias to systemctl reboot so all three are the same.

You still need to be root to do some operations using systemctl.

For the sake of beginners, would it be better to just go with this?

# Allow openhab user to execute shutdown, poweroff, and systemctl commands
openhab   ALL=(ALL) NOPASSWD: /sbin/shutdown, /sbin/poweroff, /sbin/systemctl

Or will that cause issues due to the shutdown and poweroff commands not being available in Buster?

The commands are there in Buster. They are just mapped to systemcl. But even so, I don’t think there would be a problem with sudoers if you give permission on a command that doesn’t exist.

Okay, I’ll make a note that this should work for both Jessie and Buster, but hasn’t been tested. Thanks!