+package protocol
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+)
+
+const (
+ StartTimeFormat = "2006-01-02T15:04"
+ LocalTimeFormat = "Mon _2 Jan 2006 15:04:05"
+
+ // Radio to server
+
+ AuthenticateType = "authenticate"
+ StatusType = "status"
+
+ // Server to radio
+
+ FilesType = "files"
+ PlaylistsType = "playlists"
+ StopType = "stop"
+
+ // Status values
+
+ StatusIdle = "idle"
+ StatusDelay = "delay"
+ StatusChannelInUse = "channel_in_use"
+ StatusPlaying = "playing"
+)
+
+// Base message type to determine what type of payload is expected.
+type Message struct {
+ T string
+}
+
+// Initial message from Radio to authenticate itself with a token string.
+type AuthenticateMessage struct {
+ T string
+ Token string
+}
+
+// Server updates the radio with the list of files that currently exist.
+// This will be provided on connect and when there are any changes.
+// The radio is expected to obtain all these files and cache them locally.
+type FilesMessage struct {
+ T string
+ Files []FileSpec
+}
+
+type PlaylistsMessage struct {
+ T string
+ Playlists []PlaylistSpec
+}
+
+// Any playlist currently being played should be stopped and PTT disengaged.
+type StopMessage struct {
+ T string
+}
+
+type StatusMessage struct {
+ T string
+
+ // Status w.r.t. playing a playlist
+ Status string
+
+ // File being played or about to be played - empty string in idle status
+ Filename string
+
+ // Name of playlist being played - empty string in idle status
+ Playlist string
+
+ // Seconds until playback begins - never mind latency
+ DelaySecondsRemaining int
+
+ // Number of seconds file has been actually playing
+ PlaybackSecondsElapsed int
+
+ // Number of seconds waiting for channel to clear
+ WaitingForChannelSeconds int
+
+ PTT bool
+ COS bool
+ FilesInSync bool
+
+ // Timestamp of the current time on this radio, using LocalTimeFormat
+ LocalTime string
+
+ // Time zone in use, e.g. "Australia/Hobart"
+ TimeZone string
+}
+
+// Description of an individual file available in the broadcasting system.
+type FileSpec struct {
+ // Filename, e.g. "broadcast.wav"
+ Name string
+ // SHA-256 hash of the file's contents
+ Hash string
+}
+
+type PlaylistSpec struct {
+ Id int
+ Name string
+ StartTime string
+ Entries []EntrySpec
+}
+
+type EntrySpec struct {
+ Filename string
+ DelaySeconds int
+ IsRelative bool
+}
+
+func ParseMessage(data []byte) (string, interface{}, error) {
+ var t Message
+ err := json.Unmarshal(data, &t)
+ if err != nil {
+ return "", nil, err
+ }
+
+ if t.T == AuthenticateType {
+ var auth AuthenticateMessage
+ err = json.Unmarshal(data, &auth)
+ if err != nil {
+ return "", nil, err
+ }
+ return t.T, auth, nil
+ }
+
+ if t.T == FilesType {
+ var files FilesMessage
+ err = json.Unmarshal(data, &files)
+ if err != nil {
+ return "", nil, err
+ }
+ return t.T, files, nil
+ }
+
+ if t.T == PlaylistsType {
+ var playlists PlaylistsMessage
+ err = json.Unmarshal(data, &playlists)
+ if err != nil {
+ return "", nil, err
+ }
+ return t.T, playlists, nil
+ }
+
+ if t.T == StatusType {
+ var status StatusMessage
+ err = json.Unmarshal(data, &status)
+ if err != nil {
+ return "", nil, err
+ }
+ return t.T, status, nil
+ }
+
+ if t.T == StopType {
+ var stop StopMessage
+ err = json.Unmarshal(data, &stop)
+ if err != nil {
+ return "", nil, err
+ }
+ return t.T, stop, nil
+ }
+
+ return "", nil, errors.New(fmt.Sprintf("unknown message type %v", t.T))
+}