Add option to speak weather after the given time
This commit is contained in:
parent
25fbee5a57
commit
3119204f15
1 changed files with 138 additions and 20 deletions
142
main.go
142
main.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -20,11 +22,12 @@ import (
|
||||||
"github.com/faiface/beep/flac"
|
"github.com/faiface/beep/flac"
|
||||||
"github.com/faiface/beep/mp3"
|
"github.com/faiface/beep/mp3"
|
||||||
"github.com/faiface/beep/speaker"
|
"github.com/faiface/beep/speaker"
|
||||||
|
"github.com/faiface/beep/wav"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
SampleRate = beep.SampleRate(48000)
|
|
||||||
MaxRunTime = 1 * time.Hour
|
MaxRunTime = 1 * time.Hour
|
||||||
|
ntick int64 = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
func xPrintIdle() (idle uint64) {
|
func xPrintIdle() (idle uint64) {
|
||||||
|
@ -43,8 +46,94 @@ func xPrintIdle() (idle uint64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func speakToday() {
|
||||||
|
cmdSetVolume := exec.Command("amixer", "-D", "pulse", "set", "Master", fmt.Sprintf("%d%%", 50+(ntick/int64(MaxRunTime.Seconds()/3))*50))
|
||||||
|
if err := cmdSetVolume.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd0 := exec.Command("/home/nemunaire/scripts/wakeup/today.sh")
|
||||||
|
if err := cmd0.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdSetBackVolume := exec.Command("amixer", "-D", "pulse", "set", "Master", "100%")
|
||||||
|
if err := cmdSetBackVolume.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func speakWeather() {
|
||||||
|
var icon = "partly-cloudy-day"
|
||||||
|
|
||||||
|
cmd := exec.Command("/home/nemunaire/scripts/wakeup/ind_weather.sh")
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
icon = string(out.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
var preset = "pika-summer-forest.xml"
|
||||||
|
switch icon {
|
||||||
|
case "clear-day", "clear-night":
|
||||||
|
preset = "grassland.xml"
|
||||||
|
case "rain", "hail":
|
||||||
|
preset = "the-perfect-storm.xml"
|
||||||
|
case "snow", "sleet":
|
||||||
|
preset = "a-walk-in-the-cold.xml"
|
||||||
|
case "wind", "tornado":
|
||||||
|
preset = "desert-wind.xml"
|
||||||
|
case "fog":
|
||||||
|
preset = "silent-hill-fog-world.xml"
|
||||||
|
case "cloudy":
|
||||||
|
preset = "autumn-forest.xml"
|
||||||
|
case "thunderstorm":
|
||||||
|
preset = "heavy-thunderstorm-for-me.xml"
|
||||||
|
default:
|
||||||
|
preset = "autumn-forest.xml"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdAmbiant := exec.Command("python3", "ambient.py", path.Join("presets", preset))
|
||||||
|
cmdAmbiant.Dir = "/home/nemunaire/workspace/pyambientmixer"
|
||||||
|
if err := cmdAmbiant.Start(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdSetVolume := exec.Command("amixer", "-D", "pulse", "set", "Master", fmt.Sprintf("%d%%", 50+(ntick/int64(MaxRunTime.Seconds()/3))*50))
|
||||||
|
if err := cmdSetVolume.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd0 := exec.Command("/home/nemunaire/scripts/wakeup/today.sh")
|
||||||
|
if err := cmd0.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd1 := exec.Command("/home/nemunaire/scripts/wakeup/weather.sh")
|
||||||
|
if err := cmd1.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd2 := exec.Command("/home/nemunaire/scripts/wakeup/airparif.sh")
|
||||||
|
if err := cmd2.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmdAmbiant.Process != nil {
|
||||||
|
(*cmdAmbiant.Process).Kill()
|
||||||
|
}
|
||||||
|
cmdAmbiant.Process.Wait()
|
||||||
|
|
||||||
|
cmdSetBackVolume := exec.Command("amixer", "-D", "pulse", "set", "Master", "100%")
|
||||||
|
if err := cmdSetBackVolume.Run(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadFile(path string) (s beep.StreamSeekCloser, format beep.Format, err error) {
|
func loadFile(path string) (s beep.StreamSeekCloser, format beep.Format, err error) {
|
||||||
for _, decoder := range []func(io.ReadCloser) (beep.StreamSeekCloser, beep.Format, error){flac.Decode, mp3.Decode} {
|
for _, decoder := range []func(io.ReadCloser) (beep.StreamSeekCloser, beep.Format, error){flac.Decode, mp3.Decode, wav.Decode} {
|
||||||
var fd *os.File
|
var fd *os.File
|
||||||
|
|
||||||
fd, err = os.Open(path)
|
fd, err = os.Open(path)
|
||||||
|
@ -68,6 +157,10 @@ func loadFile(path string) (s beep.StreamSeekCloser, format beep.Format, err err
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var weatherTime = flag.Duration("weather", -1, "Speak weather?")
|
||||||
|
var noshuffle = flag.Bool("noshuffle", false, "Don't shuffle music order")
|
||||||
|
var sr = flag.Int("samplerate", 44100, "Samplerate for unifying output stream")
|
||||||
|
flag.DurationVar(&MaxRunTime, "maxruntime", MaxRunTime, "Maximum duration before auto exit")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if len(flag.Args()) < 1 {
|
if len(flag.Args()) < 1 {
|
||||||
|
@ -75,12 +168,15 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
playlist := []beep.StreamSeekCloser{}
|
sampleRate := beep.SampleRate(*sr)
|
||||||
|
|
||||||
|
playlist := []beep.Streamer{}
|
||||||
formats := []beep.Format{}
|
formats := []beep.Format{}
|
||||||
|
|
||||||
// Load playlist
|
// Load playlist
|
||||||
|
log.Println("Loading playlist...")
|
||||||
for _, arg := range flag.Args() {
|
for _, arg := range flag.Args() {
|
||||||
s, f, err := loadFile(arg)
|
s, f, err := loadFile(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,43 +187,63 @@ func main() {
|
||||||
formats = append(formats, f)
|
formats = append(formats, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if noshuffle == nil || *noshuffle == false {
|
||||||
|
log.Println("Shuffling playlist...")
|
||||||
// Shuffle the playlist
|
// Shuffle the playlist
|
||||||
rand.Shuffle(len(playlist), func(i, j int) {
|
rand.Shuffle(len(playlist), func(i, j int) {
|
||||||
playlist[i], playlist[j] = playlist[j], playlist[i]
|
playlist[i], playlist[j] = playlist[j], playlist[i]
|
||||||
formats[i], formats[j] = formats[j], formats[i]
|
formats[i], formats[j] = formats[j], formats[i]
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var launched time.Time
|
||||||
|
var volume *effects.Volume
|
||||||
|
|
||||||
|
dontUpdateVolume := false
|
||||||
|
hasSpokeWeather := weatherTime == nil || *weatherTime == -1
|
||||||
|
|
||||||
// Create infinite stream
|
// Create infinite stream
|
||||||
playedItem := -1
|
playedItem := -1
|
||||||
stream := beep.Iterate(func() beep.Streamer {
|
stream := beep.Iterate(func() beep.Streamer {
|
||||||
|
if !hasSpokeWeather && time.Since(launched) >= *weatherTime {
|
||||||
|
log.Println("weather time!")
|
||||||
|
hasSpokeWeather = true
|
||||||
|
return beep.Callback(speakWeather)
|
||||||
|
}
|
||||||
|
|
||||||
|
dontUpdateVolume = false
|
||||||
|
volume.Volume = -2 - math.Log(5/float64(ntick))/3
|
||||||
playedItem += 1
|
playedItem += 1
|
||||||
if playedItem >= len(playlist) {
|
if playedItem >= len(playlist) {
|
||||||
playedItem = 0
|
playedItem = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i, ok := playlist[playedItem].(beep.StreamSeekCloser); ok {
|
||||||
// In case of loop, ensure we are at the beginning of the stream
|
// In case of loop, ensure we are at the beginning of the stream
|
||||||
playlist[playedItem].Seek(0)
|
i.Seek(0)
|
||||||
|
}
|
||||||
|
|
||||||
// Resample if needed
|
// Resample if needed
|
||||||
if formats[playedItem].SampleRate != SampleRate {
|
log.Println("playing list item:", playedItem, "/", len(playlist))
|
||||||
return beep.Resample(3, formats[playedItem].SampleRate, SampleRate, playlist[playedItem])
|
if formats[playedItem].SampleRate != sampleRate {
|
||||||
|
return beep.Resample(3, formats[playedItem].SampleRate, sampleRate, playlist[playedItem])
|
||||||
} else {
|
} else {
|
||||||
return playlist[playedItem]
|
return playlist[playedItem]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Prepare sound player
|
// Prepare sound player
|
||||||
speaker.Init(SampleRate, SampleRate.N(time.Second/10))
|
log.Println("Initializing sound player...")
|
||||||
|
speaker.Init(sampleRate, sampleRate.N(time.Second/10))
|
||||||
|
|
||||||
volume := &effects.Volume{stream, 10, -2, false}
|
volume = &effects.Volume{stream, 10, -2, false}
|
||||||
speaker.Play(volume)
|
speaker.Play(volume)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second)
|
ticker := time.NewTicker(time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
launched := time.Now()
|
launched = time.Now()
|
||||||
idle := xPrintIdle()
|
idle := xPrintIdle()
|
||||||
ntick := 0
|
|
||||||
|
|
||||||
// Prepare graceful shutdown
|
// Prepare graceful shutdown
|
||||||
maxRun := time.After(MaxRunTime)
|
maxRun := time.After(MaxRunTime)
|
||||||
|
@ -141,7 +257,9 @@ loop:
|
||||||
break loop
|
break loop
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
ntick += 1
|
ntick += 1
|
||||||
|
if !dontUpdateVolume {
|
||||||
volume.Volume = -2 - math.Log(5/float64(ntick))/3
|
volume.Volume = -2 - math.Log(5/float64(ntick))/3
|
||||||
|
}
|
||||||
|
|
||||||
if xPrintIdle() < idle {
|
if xPrintIdle() < idle {
|
||||||
break loop
|
break loop
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue