2018-10-24 14:33:21 +00:00
|
|
|
# sonar
|
|
|
|
|
2018-10-24 17:12:43 +00:00
|
|
|
Sonar is an alarm clock server designed to run on a Raspberry Pi, but it could
|
|
|
|
just as well work anywhere.
|
|
|
|
|
|
|
|
For the alarm clock it plays a list of media files from the filesystem. By
|
|
|
|
default it will use the `mplayer` command.
|
|
|
|
|
|
|
|
# Usage
|
|
|
|
|
|
|
|
```
|
|
|
|
./sonar [-listen 127.0.0.1:8000]
|
|
|
|
```
|
|
|
|
|
|
|
|
Sonar has no authentication system. It listens on localhost by default and you
|
|
|
|
should put a proxy like nginx in front with HTTP Basic Auth or whatever.
|
|
|
|
|
|
|
|
It will create its config on first startup.
|
|
|
|
|
|
|
|
# Features
|
|
|
|
|
|
|
|
It listens on an HTTP service and shows a GUI on the homepage where you can
|
|
|
|
toggle the volume settings, start/stop the alarm clock playlist, and see/change
|
|
|
|
the scheduled alarm times.
|
|
|
|
|
|
|
|
The clock is controlled over simple RESTful API. You just post to these
|
|
|
|
endpoints:
|
|
|
|
|
|
|
|
* `/volume/higher`: increase volume by 5% (default)
|
|
|
|
* `/volume/lower`: lower volume by 5%
|
|
|
|
* `/volume/mute`: toggle mute status
|
|
|
|
* `/playlist/start`: start the playlist (doesn't stop automatically!)
|
|
|
|
* `/playlist/stop`: stop the playlist
|
|
|
|
|
|
|
|
Example to start the playlist via `curl`:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
$ curl -X POST http://localhost:8000/playlist/start
|
|
|
|
```
|
|
|
|
|
|
|
|
# Screenshots
|
|
|
|
|
|
|
|
![screenshot1.png](screenshot1.png)![screenshot2.png](screenshot2.png)
|
|
|
|
|
|
|
|
# How It Works
|
|
|
|
|
|
|
|
The config file specifies the shell commands to run to launch your media player,
|
|
|
|
volume changing commands, etc.
|
|
|
|
|
|
|
|
When the playlist starts, the Go app shuffles the files in your media folder
|
|
|
|
and feeds them one by one to your media player command (`mplayer` by default).
|
|
|
|
To stop the playlist, it kills the current mplayer task and stops.
|
|
|
|
|
|
|
|
When you save a schedule for the alarm clock, it will create and install a
|
|
|
|
crontab entry for the user running the app. The cron entry hits the API server
|
|
|
|
to start the playlist at the desired time, and then, an hour later, it stops
|
|
|
|
it the same way.
|
|
|
|
|
|
|
|
## Crontab
|
|
|
|
|
|
|
|
The schedule system installs into the user's local crontab. The cron entries
|
|
|
|
just post back to the API service, like:
|
|
|
|
|
|
|
|
```cron
|
|
|
|
30 5 * * * curl -X POST http://127.0.0.1:8000/playlist/start
|
|
|
|
30 6 * * * curl -X POST http://127.0.0.1:8000/playlist/stop
|
|
|
|
```
|
|
|
|
|
|
|
|
The stop command is installed one hour after the start.
|
|
|
|
|
|
|
|
The user's local crontab is **overwritten** by the one Sonar installs. To keep
|
|
|
|
custom crontab entries, place them into the `crontab.in/` directory.
|
|
|
|
|
|
|
|
All custom user crontabs are concatenated together ahead of Sonar's cron entries.
|
|
|
|
The `000-header.cron` is the standard Debian cron header and tends to be installed
|
|
|
|
on top.
|
|
|
|
|
|
|
|
# Installation
|
|
|
|
|
|
|
|
## Supervisor
|
|
|
|
|
|
|
|
There's an example supervisor config in the `etc/` folder.
|
|
|
|
|
|
|
|
Add it to supervisor and put nginx in front with Basic Auth.
|
|
|
|
|
|
|
|
# Makefile
|
|
|
|
|
|
|
|
* `make setup` to fetch dependencies.
|
|
|
|
* `make build` to build the binary to `bin/`
|
|
|
|
* `make dist` to build a distribution for your current setup
|
|
|
|
* `make run` to run it in debug mode
|
|
|
|
* `make watch` to run it in debug mode, auto-reloading (sometimes flaky control over mplayer tho!)
|
|
|
|
* `make pi` to build a zipped distribution for Raspberry Pi.
|
|
|
|
See [Cross Compile for Raspberry Pi](#cross-compile-for-raspberry-pi)
|
|
|
|
|
|
|
|
# Configuration
|
|
|
|
|
|
|
|
The config file will be in your system's native location, which is
|
|
|
|
`~/.config/sonar.json` on Linux environments.
|
|
|
|
|
|
|
|
After running the app once, it will save its default configuration to disk.
|
|
|
|
The defaults are fine for PulseAudio setups but you may want to revise it to
|
|
|
|
be sure.
|
|
|
|
|
|
|
|
A default config file looks like this, annotated:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
{
|
|
|
|
"cookieName": "session", // name of HTTP session cookie
|
|
|
|
"mediaPath": "./media", // path of media files (like .mp3) to shuffle and play
|
|
|
|
"mediaCommand": [
|
|
|
|
// The command to actually play the media. Use %s where the filename goes.
|
|
|
|
"mplayer",
|
|
|
|
"%s"
|
|
|
|
],
|
|
|
|
"volUpCommand": [
|
|
|
|
"pactl",
|
|
|
|
"set-sink-volume",
|
|
|
|
"0", // Sink number, from `pactl list-sinks`
|
|
|
|
"+5%" // 5% step
|
|
|
|
],
|
|
|
|
"volDnCommand": [
|
|
|
|
"pactl",
|
|
|
|
"set-sink-volume",
|
|
|
|
"0", // Sink number
|
|
|
|
"-5%"
|
|
|
|
],
|
|
|
|
"volMuteCommand": [
|
|
|
|
"pactl",
|
|
|
|
"set-sink-mute",
|
|
|
|
"0",
|
|
|
|
"toggle"
|
|
|
|
],
|
|
|
|
"volStatusCommand": [
|
|
|
|
// How to get the volume status. The command
|
|
|
|
// should output just a value like: 56%
|
|
|
|
"bash",
|
|
|
|
"-c",
|
|
|
|
"pacmd dump-volumes | grep \"Sink 0\" | egrep -o '([0-9]+)%' | head -1"
|
|
|
|
],
|
|
|
|
// scheduled alarm time by default
|
|
|
|
"hour": 6,
|
|
|
|
"minute": 30,
|
|
|
|
"days": [
|
|
|
|
"1", "2", "3", "4", "5"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2019-02-01 01:04:49 +00:00
|
|
|
## ALSA Driver HOW-TO on Raspberry Pi
|
|
|
|
|
|
|
|
The default config above is generated assuming a PulseAudio setup, but the
|
|
|
|
current Raspbian images for Raspberry Pi prefer ALSA by default. Here are some
|
|
|
|
steps to identify your sound card and edit sonar.json accordingly.
|
|
|
|
|
|
|
|
### List ALSA Devices
|
|
|
|
|
|
|
|
The `alsa-utils` package provides a handful of commands you might already
|
|
|
|
have pre-installed.
|
|
|
|
|
|
|
|
In my use case, I had a USB audio device attached to my Raspberry Pi that
|
|
|
|
shows up in ALSA as a separate hardware device:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
$ aplay -l
|
|
|
|
**** List of PLAYBACK Hardware Devices ****
|
|
|
|
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
|
|
|
|
Subdevices: 7/7
|
|
|
|
Subdevice #0: subdevice #0
|
|
|
|
Subdevice #1: subdevice #1
|
|
|
|
Subdevice #2: subdevice #2
|
|
|
|
Subdevice #3: subdevice #3
|
|
|
|
Subdevice #4: subdevice #4
|
|
|
|
Subdevice #5: subdevice #5
|
|
|
|
Subdevice #6: subdevice #6
|
|
|
|
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
|
|
|
|
Subdevices: 1/1
|
|
|
|
Subdevice #0: subdevice #0
|
|
|
|
card 1: Device [USB2.0 Device], device 0: USB Audio [USB Audio]
|
|
|
|
Subdevices: 1/1
|
|
|
|
Subdevice #0: subdevice #0
|
|
|
|
```
|
|
|
|
|
|
|
|
The `aplay -l` command lists your ALSA devices.
|
|
|
|
|
|
|
|
Another command to know is `alsamixer` which presents a graphical CLI
|
|
|
|
program to adjust the volume and show sound card details.
|
|
|
|
|
|
|
|
### Volume Control with ALSA
|
|
|
|
|
|
|
|
The `amixer` command can adjust volume settings. For simple setups you
|
|
|
|
can use commands like:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# Increase or decrease volume by 10% at a time, respectively
|
|
|
|
$ amixer set Master 10%+
|
|
|
|
$ amixer set Master 10%-
|
|
|
|
|
|
|
|
# Toggle mute on or off
|
|
|
|
$ amixer set Master toggle
|
|
|
|
|
|
|
|
# Get the current volume setting
|
|
|
|
$ awk -F"[][]" '/dB/ { print $2 }' <(amixer sget Master)
|
|
|
|
100%
|
|
|
|
```
|
|
|
|
|
|
|
|
In my case I needed to also specify the sound card device
|
|
|
|
(`-c 1`) because I'm not using the default, and my output channel is named
|
|
|
|
PCM instead of Master on that device:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
$ amixer -c 1 set PCM 10%+
|
|
|
|
$ amixer -c 1 set PCM 10%-
|
|
|
|
$ amixer -c 1 set PCM toggle
|
|
|
|
$ awk -F"[][]" '/dB/ { print $2 }' <(amixer -c 1 sget PCM)
|
|
|
|
```
|
|
|
|
|
|
|
|
### MPlayer with ALSA Devices
|
|
|
|
|
|
|
|
I also needed to sort out the `mplayer` command to tell it to use my
|
|
|
|
ALSA device:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
$ mplayer -ao alsa:device=hw=1.0 <filename>
|
|
|
|
```
|
|
|
|
|
|
|
|
### Example ALSA Config File
|
|
|
|
|
|
|
|
My sonar.json config now looks like:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"cookieName": "session",
|
|
|
|
"mediaPath": "/home/kirsle/Music/watts",
|
|
|
|
"mediaCommand": [
|
|
|
|
"mplayer",
|
|
|
|
"-ao",
|
|
|
|
"alsa:device=hw=1.0",
|
|
|
|
"%s"
|
|
|
|
],
|
|
|
|
"volUpCommand": [
|
|
|
|
"amixer",
|
|
|
|
"-c",
|
|
|
|
"1",
|
|
|
|
"set",
|
|
|
|
"PCM",
|
|
|
|
"10%+"
|
|
|
|
],
|
|
|
|
"volDnCommand": [
|
|
|
|
"amixer",
|
|
|
|
"-c",
|
|
|
|
"1",
|
|
|
|
"set",
|
|
|
|
"PCM",
|
|
|
|
"10%-"
|
|
|
|
],
|
|
|
|
"volMuteCommand": [
|
|
|
|
"amixer",
|
|
|
|
"-c",
|
|
|
|
"1",
|
|
|
|
"set",
|
|
|
|
"PCM",
|
|
|
|
"toggle"
|
|
|
|
],
|
|
|
|
"volStatusCommand": [
|
|
|
|
"bash",
|
|
|
|
"-c",
|
|
|
|
"awk -F\"[][]\" '/dB/ { print $2 }' \u003c(amixer -c 1 sget PCM) egrep -o '([0-9]+)%' | head -1"
|
|
|
|
],
|
|
|
|
"hour": 6,
|
|
|
|
"minute": 30,
|
|
|
|
"days": [
|
|
|
|
"1",
|
|
|
|
"2",
|
|
|
|
"3",
|
|
|
|
"4",
|
|
|
|
"5"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-10-24 17:12:43 +00:00
|
|
|
## Cross Compile for Raspberry Pi
|
|
|
|
|
|
|
|
Use the `make pi` command to build a distribution for Raspberry Pi.
|
|
|
|
|
|
|
|
If you get permission errors when trying to download the standard library for
|
|
|
|
ARM64, make and chown the folders as a workaround:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sudo mkdir /usr/lib/golang/pkg/linux_arm
|
|
|
|
sudo chown kirsle:kirsle /usr/lib/golang/pkg/linux_arm
|
|
|
|
make pi
|
|
|
|
rsync -av sonar.pi 192.168.0.102:
|
|
|
|
```
|
|
|
|
|
|
|
|
It outputs a `sonar-pi.zip` that you can scp over and run.
|
|
|
|
|
|
|
|
# License
|
|
|
|
|
|
|
|
Noah Petherbridge © 2018
|
|
|
|
|
|
|
|
GPLv2.
|