Frames

Starter

0
1
2
1package main
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "flag"
8 "fmt"
9 "io/ioutil"
10 "log"
11 "os"
12 "os/exec"
13 "strings"
14 "time"
15
16 "github.com/mohanarpit/wwe-entrance/router"
17)
18
19// Config is the data representation of the config.json file where the user can define the
20// devices for which music must be played
21type Config struct {
22 MacAddress string `json:"mac_address"`
23 SoundFile string `json:"sound_file"`
24}
25
26// MusicInfo is the data representation for passing the actual song file and command to be used
27type MusicInfo struct {
28 SoundFile string
29 Command string
30}
31
32// DeviceInfo is the data representation of the parsed arp output
33type DeviceInfo struct {
34 IP string
35 MacAddress string
36}
37
38// DeviceMap is the map of devices to their mac address. Makes it easy for us to check if a device is new
39type DeviceMap map[string]DeviceInfo
40
41type macAddresses []string
42
43func (m *macAddresses) String() string {
44 return "These are the mac ids"
45}
46
47func (m *macAddresses) Set(value string) error {
48 *m = append(*m, value)
49 return nil
50}
51
52func parsePropertyFile(filename string) (config []Config, err error) {
53 jsonFile, err := os.Open(filename)
54 if err != nil {
55 return nil, err
56 }
57
58 defer jsonFile.Close()
59 byteValue, _ := ioutil.ReadAll(jsonFile)
60 json.Unmarshal(byteValue, &config)
61 return config, err
62}
63
64func parseArpOutput(output string) (deviceMap DeviceMap, err error) {
65 if deviceMap == nil {
66 deviceMap = make(map[string]DeviceInfo)
67 }
68
69 // Parse the output of ARP Command
70 lines := strings.Split(output, "\n")
71 for _, line := range lines {
72 // This function tokenizes the line by any number of whitespaces whitespaces
73 fields := strings.Fields(line)
74
75 if len(fields) < 4 {
76 continue
77 }
78
79 flag := fields[2]
80 // 0x2 means the device is connected
81 if flag != "0x2" {
82 continue
83 }
84
85 device := DeviceInfo{
86 IP: fields[0],
87 MacAddress: fields[3],
88 }
89 deviceMap[device.MacAddress] = device
90 }
91 return deviceMap, nil
92}
93
94func getSoundFile(device DeviceInfo, configs []Config) string {
95 for _, config := range configs {
96 if device.MacAddress == config.MacAddress {
97 return config.SoundFile
98 }
99 }
100 return ""
101}
102
103func playMusic(musicChannel <-chan MusicInfo, errChan chan<- error) {
104 for musicInfo := range musicChannel {
105 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
106 defer cancel()
107
108 musicCmd := exec.CommandContext(ctx, "sh", "-c", musicInfo.Command+" "+musicInfo.SoundFile)
109 var out bytes.Buffer
110 var stderr bytes.Buffer
111 musicCmd.Stdout = &out
112 musicCmd.Stderr = &stderr
113 err := musicCmd.Run()
114 if err != nil {
115 log.Printf(fmt.Sprint(err) + ": " + stderr.String())
This is the entry function to this codebase

116 errChan <- err
117 }
118 if ctx.Err() == context.DeadlineExceeded {
119 log.Println("Deadline exceeded", ctx.Err())
120 errChan <- err
121 }
122 }
123}
124
125func getGatewayAddress() string {
126 //TODO: Get the broadcast IP instead of hardcoding it here
127 return "192.168.0.1:23"
128}
129
130func main() {
131 var defaultAudioCmd = flag.String("default-audio", "/usr/local/bin/vlc", "Default audio player command")
132 var routerUsername = flag.String("username", "Admin", "The username for your router login")
133 var routerPwd = flag.String("password", "Password", "The password for your router login")
134 var propertyFile = flag.String("property-file", "config.json", "The location of the property file")
135 var delay = flag.Duration("delay", 5*time.Second, "The delay (in seconds) with which the program will attempt to poll the router for connected devices")
136 flag.Parse()
137
138 config, err := parsePropertyFile(*propertyFile)
139 if err != nil {
140 log.Fatalln(err)
141 }
142
143 // Initialize the musicInfo channel
144 musicChannel := make(chan MusicInfo)
145 errChan := make(chan error)
146 go playMusic(musicChannel, errChan)
147
148 //Connect to the router
149 dlink := router.DlinkRouter{
150 ConnectionType: "tcp",
151 Command: "cat /proc/net/arp",
152 }
153
154 conn, err := dlink.Connect(*routerUsername, *routerPwd, getGatewayAddress(), *delay)
155 if err != nil {
156 log.Fatalln(err)
157 }
158 defer conn.Close()
159
160 // Run the program periodically to check for new devices
161 tick := time.Tick(*delay)
162 var oldDevices DeviceMap
163
164 for {
165 select {
166 case <-tick:
167 output, err := dlink.GetArpOutput(conn)
168 if err != nil {
169 log.Printf("Error in connecting to router: %+v", err)
170 return
171 }
172
173 devices, _ := parseArpOutput(output)
174 log.Println("Devices: ", devices)
175
176 // Play the music only if a new device is connecting
177 for ip, device := range devices {
178 if _, ok := oldDevices[ip]; !ok {
179 soundFile := getSoundFile(device, config)
180 if soundFile != "" {
181 // This is a new device connecting
182 musicInfo := MusicInfo{
183 SoundFile: soundFile,
184 Command: *defaultAudioCmd,
185 }
186 musicChannel <- musicInfo
187 }
188 }
189 }
190 oldDevices = devices
191 case err := <-errChan:
192 log.Fatal(err)
193 }
194 }
195}
196