-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial commit & basic features
- Loading branch information
0 parents
commit a5c419e
Showing
5 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mpzbc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# mpzbc - music player zigbee client | ||
[zigbee2mqtt](https://www.zigbee2mqtt.io) + [IKEA E1744 SYMFONISK sound controller](https://www.zigbee2mqtt.io/devices/E1744.html) + mpzbc == wireless mpd remote | ||
|
||
## Usage | ||
``` | ||
go get github.com/feuerrot/mpzbc | ||
MQTTSERVER=mqtthost:1883 MQTTTOPIC=zigbee2mqtt/friendly_e1744_name MPDSERVER=mpdhost:6600 mpzbc | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module github.com/feuerrot/mpzbc | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/eclipse/paho.mqtt.golang v1.2.0 | ||
github.com/fhs/gompd v1.0.1 | ||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0= | ||
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= | ||
github.com/fhs/gompd v1.0.1 h1:kBcAhjnAPJQAylZXR0TeH+d2vpjawXlTtKYguqNlF4A= | ||
github.com/fhs/gompd v1.0.1/go.mod h1:b219/mNa9PvRqvkUip51b23hGL3iX4d4q3gNXdtrD04= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= | ||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"time" | ||
|
||
mqtt "github.com/eclipse/paho.mqtt.golang" | ||
"github.com/fhs/gompd/mpd" | ||
) | ||
|
||
const volumedelta int = 5 | ||
|
||
type mpzbc struct { | ||
mqttClient mqtt.Client | ||
mqttServer string | ||
mqttTopic string | ||
mpdClient *mpd.Client | ||
mpdServer string | ||
mpdStatus string | ||
mpdVolume int | ||
} | ||
|
||
type control struct { | ||
Action string | ||
Battery int | ||
Linkquality int | ||
} | ||
|
||
func (m *mpzbc) mqttMessage(client mqtt.Client, msg mqtt.Message) { | ||
fmt.Printf("Message: %s\n", msg.Payload()) | ||
m.updateMPD() | ||
|
||
ctrl := control{} | ||
if err := json.Unmarshal(msg.Payload(), &ctrl); err != nil { | ||
fmt.Printf("Unmarshal error: %v", err) | ||
} | ||
|
||
switch ctrl.Action { | ||
case "play_pause": | ||
if m.mpdStatus == "play" { | ||
m.mpdClient.Pause(true) | ||
} else { | ||
m.mpdClient.Play(-1) | ||
} | ||
case "rotate_left": | ||
m.updateMPD() | ||
if m.mpdVolume != -1 { | ||
m.mpdClient.SetVolume(m.mpdVolume - volumedelta) | ||
} | ||
case "rotate_right": | ||
m.updateMPD() | ||
if m.mpdVolume != -1 { | ||
m.mpdClient.SetVolume(m.mpdVolume + volumedelta) | ||
} | ||
case "skip_backward": | ||
m.mpdClient.Previous() | ||
case "skip_forward": | ||
m.mpdClient.Next() | ||
} | ||
} | ||
|
||
func (m *mpzbc) connectMQTT() error { | ||
fmt.Println("Build MQTT Client") | ||
co := mqtt.NewClientOptions() | ||
co.AddBroker("tcp://" + m.mqttServer) | ||
co.SetClientID(fmt.Sprintf("mpzbc_%x", os.Getpid())) | ||
co.SetAutoReconnect(true) | ||
co.SetCleanSession(true) | ||
|
||
co.OnConnect = func(c mqtt.Client) { | ||
if token := c.Subscribe(m.mqttTopic, 0, m.mqttMessage); token.Wait() && token.Error() != nil { | ||
fmt.Printf("error during mqtt subscribe: %v\n", token.Error()) | ||
} | ||
} | ||
|
||
client := mqtt.NewClient(co) | ||
fmt.Println("Connect to MQTT") | ||
if token := client.Connect(); token.Wait() && token.Error() != nil { | ||
return fmt.Errorf("error during mqtt connect: %v", token.Error()) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (m *mpzbc) updateMPD() error { | ||
status, err := m.mpdClient.Status() | ||
if err != nil { | ||
return fmt.Errorf("couldn't get MPD status: %v", err) | ||
} | ||
|
||
state, ok := status["state"] | ||
if !ok { | ||
return fmt.Errorf("no state in MPD status") | ||
} | ||
m.mpdStatus = state | ||
|
||
volume, ok := status["volume"] | ||
if !ok { | ||
m.mpdVolume = -1 | ||
} else { | ||
m.mpdVolume, err = strconv.Atoi(volume) | ||
if err != nil { | ||
return fmt.Errorf("couldn't convert %s to integer: %v", volume, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (m *mpzbc) connectMPD() error { | ||
mpdClient, err := mpd.Dial("tcp", m.mpdServer) | ||
if err != nil { | ||
return fmt.Errorf("error while connecting to %s: %v", m.mpdServer, err) | ||
} | ||
m.mpdClient = mpdClient | ||
|
||
return nil | ||
} | ||
|
||
func (m *mpzbc) getEnv() error { | ||
m.mqttServer = os.Getenv("MQTTSERVER") | ||
if m.mqttServer == "" { | ||
return fmt.Errorf("MQTTSERVER is empty") | ||
} | ||
|
||
m.mqttTopic = os.Getenv("MQTTTOPIC") | ||
if m.mqttTopic == "" { | ||
return fmt.Errorf("MQTTTOPIC is empty") | ||
} | ||
|
||
m.mpdServer = os.Getenv("MPDSERVER") | ||
if m.mpdServer == "" { | ||
return fmt.Errorf("MPDSERVER is empty") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (m *mpzbc) run() error { | ||
if err := m.getEnv(); err != nil { | ||
return fmt.Errorf("couldn't get settings: %v", err) | ||
} | ||
if err := m.connectMPD(); err != nil { | ||
return fmt.Errorf("couldn't connect to MPD: %v", err) | ||
} | ||
if err := m.connectMQTT(); err != nil { | ||
return fmt.Errorf("couldn't connect to MQTT: %v", err) | ||
} | ||
for { | ||
if err := m.updateMPD(); err != nil { | ||
return fmt.Errorf("couldn't update MPD state: %v", err) | ||
} | ||
fmt.Printf("state: %s\tvolume: %d\n", m.mpdStatus, m.mpdVolume) | ||
time.Sleep(1 * time.Second) | ||
} | ||
} | ||
|
||
func main() { | ||
client := mpzbc{} | ||
if err := client.run(); err != nil { | ||
fmt.Printf("error during client.run(): %v\n", err) | ||
} | ||
} |