How to switch to MacOS

How to switch over to MacOS

BIG EDIT: I upgraded to a Mac Mini with an M2 chip, so I took the opportunity to switch over to HA supervised, which simplified a lot of things due to the access to add-ons (nginx, AdGuard, Grafana, etc.). I used this guide after this guide didn’t work. All of the tricks that I talk about in a separate post still work by SSHing from the VM into the host. I’m pretty sure that the same install (which requires Debian running in its own virtual machine) would be more or less similar and work the same way. The one gotcha I encountered was to make sure that you select the correct network interface for the UTM virtual machine.

Like many of us here, I started as a newb and had to learn how to SSH into a Raspberry Pi, write basic YAML and effectively use the command line. As soon as I thought I had it all under control, I decided to switch over to a Mac Mini; unfortunately, however, this task isn’t as easy as copying over one’s HA config files, and there’s no real guide that I could find. It took a lot of messing around to figure it out, so I thought I might put a little PSA out for anyone else embarking on this process without the full knowledge of how to do so. Before I even start, I’m barely more than a noob, so take what you see with a grain of salt.

First off, why switch? Briefly my mini ( I was feeling spendy, so it’s the new model) offers:

  • speed: HA graphs render instantly; restarts take ~12 seconds instead of minutes; etc.
  • functions as a NAS and media server. USB 3/Thunderbolt ports = cheap, fast storage.
  • Spotify to homepods. Airfoil.
  • Time Machine backups = no need for rsync and no worrying about SD card fails
  • HDMI out to nearby TV + wireless keyboard/trackpad (or VNC) = biggest monitor in the house

Now, things to know before you start:

  • Starting with El Capitan, and progressing with each new OS, Apple introduced some new security components with which you shall tangle. First off, “Full Disk Access” is a feature that requires you to give permission to apps- including Terminal and any emulators you may be using (Termius in my case)- permission to work with the entire disk. Go to System prefs → security & privacy → privacy, then select full disk access and add what you need. Also you may like to read about SIP (System Integrity Protection), which limits access to certain system folders. This gets even more restrictive in Catalina (see below).
  • Decide on your setup: I chose to run in a VENV because that’s how I did it on my Pi, and you can follow the standard HA installation documentation (although the autostart script to which it links didn’t work for me- see later):
    Installation - Home Assistant
    I don’t know if there’s a way to run Hassio on a MacOS volume… maybe with Docker or something.
  • Package installer: there is no apt-get on MacOS; the best alternative is Homebrew. You can read about this elsewhere, but installing an updated version of Python in your VENV is easy with Homebrew.
  • Restarting a headless Mac: at first this was really frustrating, as I was trying to run a headless mini via SSH like we do with the RPi’s, but because of full disk encryption, it turns out that issuing a restart on a mac brings you to a login screen at a point in the boot where VNC and/or the built-in screen sharing modality in Mojave or Catalina isn’t active yet. Which means that you need a physical keyboard and monitor to log in, which means that you’re not headless any more. Well it turns out that there’s a command you can issue that will take you straight to the user login screen at which point VNC will have started, and you can login in that way. The command:
    sudo fdesetup authrestart
    Also, I discovered that it IS possible to write a bash script using this command so that you have a button to restart your Mac in HA. See link in second post.

Now comes what was I think the most frustrating issue that I faced: who is HA running as, and where are the config files??? Well it turns out that this all has to do with how you START HA. I shall explain.

  • Let’s start by reviewing the fact that HA should not run as root. However, we sudo the systemctl command to start HA on a pi as detailed here:
    https://www.home-assistant.io/docs/installation/hassbian/common-tasks/
    If you sudo your start command, by using sudo you have specified that root owns the process (edit: unless you use the -u flag to specify a different user). If you don’t specify a user, HA not only runs as root but also looks here for the config files:
    /var/root/.homeassistant
    … but var is a symlink, so the actual path is:
    /private/var/root/.homeassistant
    Confused yet? I sure was. My solution to this was NOT to create a new user just for HA which would always be logged in but rather just to use the default user which, given that it has admin privileges may not be as secure as a limited user, but it’s better than root. Also, the config files wind up in a non-perplexing location at:
    /Users/yourusername/.homeassistant
  • So, how to start HA as your user? Well, ideally you want HA to start at boot, so you can use crontab, but I elected to educate myself on launchd, which is MacOS’s updated version of crontab. This involves creating a .plist file that you put in your ~/Library/LaunchAgents folder (which by virtue of its location will start as the user). Good advice here:
    Mac crontab: Creating macOS startup jobs with crontab, er, launchd | alvinalexander.com
    EDIT: see post below about creating a .plist for autostart.
    In that article he uses launchctl load/unload for start/stop, but FYI there’s even newer syntax using boostrap/bootout and even kickstart (to stop and then restart). This looks like:
    launchctl bootstrap gui/<user PID> /Users/yourusername/Library/Launchagents/org.homeassistant.plist
  • Activating your VENV looks familiar if you created it in your user folder:
    source /Users/yourusername/homeasistant/bin/activate

Dependencies

  • I had remarkably few issues here. Just remember that if you’re looking for something you would normally need apt-get for, there’s probably an analogous package in Homebrew. For example, I was looking for libssl for an updated Apple TV integration and realized that openssl was already installed and filled the bill.
  • Also remember to re-install anything that you had to install separately on your pi (like iStats in my instance).

Catalina-specific issues

  • / isn’t what it used to be, which broke a few things (like system monitor’s disk percent usage). The system now exists on a protected volume, and the path to what the user can access is:
    /system/volumes/data
  • 32 bit support is gone. Find updated versions of those old programs.
  • The default shell is now zh instead of bash, but if you upgrade an existing Mojave system your default shell doesn’t change unless you tell it to. Terminal gives you a blurb about it.

What I’m into lately

I sure hope this saves someone some time, because I wasted a lot figuring it out. Big brains please correct my errors!

10 Likes

Fun followup tricks here!

Autostart on MacOS

There’s a community guide on how to do this that appears identical to what I remember being on the main website at some point- it never worked for me and I’ve seen some people in the forums struggling with this, so as a follow up to the above, here’s how to do it with launchd. First you may like to read the link from above to gain an understanding of launchd, which is the updated method for automating jobs; the older one, cron, still works also but has been deprecated since 2005.

In order to use launchd you create a .plist file in the appropriate folder and the system will see it and run the payload, in this case at system startup. If you’ve set things up the way I did above with your user as the HA user, you would put the .plist file in /Users/YOURUSERNAME/Library/LaunchAgents. That’s it- create the file as below and make sure to proof the paths.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>AbandonProcessGroup</key>
	<false/>
	<key>EnvironmentVariables</key>
	<dict>
		<key>LC_CTYPE</key>
		<string>UTF-8</string>
		<key>PATH</key>
		<string>/usr/local/bin/:/usr/bin:/usr/sbin:/sbin:$PATH</string>
	</dict>
	<key>KeepAlive</key>
	<dict>
		<key>SuccessfulExit</key>
		<false/>
	</dict>
	<key>Label</key>
	<string>org.homeassistant</string>
	<key>Program</key>
	<string>/Users/YOURUSERNAME/homeassistant/bin/hass</string>
	<key>RunAtLoad</key>
	<true/>
	<key>StandardErrorPath</key>
	<string>/Users/YOURUSERNAME/.homeassistant/home-assistant.log</string>
	<key>StandardOutPath</key>
	<string>/Users/YOURUSERNAME/.homeassistant/home-assistant.log</string>
</dict>
</plist>

Cool, thanks.

Any idea how to restart, in it crashed or simply exited?

Especially regarding this issue:

First figure out the PID of the HA user. Then, if you’ve created a .plist as I described above…

to start HA:
launchctl bootstrap gui/<PID> /Users/HAusername/Library/LaunchAgents/org.homeassistant.plist

to stop HA:
launchctl bootout gui/<PID> /Users/HAusername/Library/LaunchAgents/org.homeassistant.plist

to restart:
launchctl kickstart -k gui/<PID>/org.homeassistant

How to set up split-brain DNS on Mac
… so you can use your HA instance if the internet is down or if your router doesn’t support NAT loopback (aka hairpin NAT). Also accessing HA is noticeably faster when on my home network.

I’ve seen some folks struggling with this in the forums, mainly it seems because of the separate external_url: and internal_url: fields in the companion app. The companion app documentation on this is actually really good but doesn’t directly apply in the Mac world, so I thought I’d post about how I did this. This probably actually applies to anyone running in a container or VENV because then we don’t have access to the Adguard add-on.
The following assumes:

  • You already read the linked companion app doc and mostly understand it
  • You already have a secured instance of HA with a certificate and a DDNS service like duckdns.org
  • You know how to SSH into your server etc etc
  • To allow you to use the same URL inside & outside your network, your port forward to HA should be 8123 → 8123 (previously I had 443 → 8123 and wasn’t appending a port number which will work with hairpin NAT, but we’re about to eliminate hairpinning). The DNS rewrites in AdGuard don’t handle ports, only IP addresses. If you’re only using the companion app outside your network then you could skip this and handle it with the internal & external URL fields (I finally understand their utility).

The first thing to know is that you can go to their site for AdGuard for Mac or install via Homebrew (which gives you the same version) if you’d like to support AdGuard with a subscription fee; maybe the features are different, I don’t know. What I installed was AdGuard Home which is free and seems to be more geared to our needs. Installation instructions are at their Github repository or, on Linux or Mac just run this script. They have instructions for Docker at that site.

curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

After the script runs it will give you an IP address that will pull up the config page and a wizard will get you going- docs here. Then you just add a DNS rewrite as per the companion app doc and- done.

Edit: removed the part about setting up encryption on AG which according to the documentation is only necessary if AG is hosted on a virtual private server. I was trying to set up DNS encryption (specifically DoH) and it looks like that is done by specifying the upstream DNS server in the DNS section (it has good examples in the interface).

Edit 2: it’s referenced in the AdGuard instructions that you need to point your router to your server for DNS but this is understandably vague since there are so many kinds of routers out there. You may have to hunt around as I had to to get this exactly right. Here’s my example for those who have ASUS routers, which have settings for DNS in two locations:

  • under Advanced Settings → WAN (two fields): this is for the router to use and changing them both to your Adguard server for some reason makes the router think it has no internet connection. It will also cause you to lose WAN access if your server is down or while rebooting, etc. I put my server in the first field and 1.1.1.1 as the failover.
  • under Advanced Settings → LAN: this is what is served to the devices on your network and is the important place to put the server URL. If you only put it under WAN as the first of two entires as above it seems to work some of the time but your metrics in AG are skewed (100% of requests from the router) and I still got a lot of ads.

So at this point there’s only one problem left and that’s that the stock ASUS firmware vexingly serves it’s own IP as a second DNS server invisibly. If you know how to SSH into your ASUS then you can follow the directions here to fix this (or alternatively install Merlin which allegedly has an option not to share the router as a DNS server on the LAN).

Edit 3: if you install NGINX on your mac and then follow the HA community guide to get it up and running, NGINX can handle port redirects and certificates (in case of further certificate errors).

Do you have any experience running shell scripts?

If I run a script by open path/script - it works, but it opens a Terminal window
If I run a script by path/script - it doesn’t work at all
If I run a script by open path/script.app (Automator App with shell script inside) - it doesn’t work, but some commands work- the gear spins for a second or so on the top bar

Post your script and let’s see

Script file delete_test contains (made executable with: chmod 711):

#!/bin/sh
rm -f ~/Desktop/test.jpg
shell_command:
  delete_test: /Users/server/.homeassistant/scripts/delete_test

You can also put it in Automator, which I want because I need the script to run longer than 10 seconds. But it will not work.

Using open /Users/server/.homeassistant/scripts/delete_test works.

While writing the post I have solved it. The paths are unknown.

/bin/rm -f ~/Desktop/test.jpg works. This seems to apply for all scripts.

I was missing /bin/ inside my LaunchAgent. I guess this was the problem.

	<key>EnvironmentVariables</key>
	<dict>
		<key>LC_CTYPE</key>
		<string>UTF-8</string>
		<key>PATH</key>
		<string>/usr/local/bin/:/usr/bin:/usr/sbin:/sbin:$PATH</string>
	</dict>

changed to

	<key>EnvironmentVariables</key>
	<dict>
		<key>LC_CTYPE</key>
		<string>UTF-8</string>
		<key>PATH</key>
		<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH</string>
	</dict>

Maybe I have to add other paths as well, not sure.