summaryrefslogtreecommitdiffstats
path: root/daemon.py
blob: 91b38e8764affebf8748eb72d4b88f1d38dac495 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/python

import serial
import mpd
import time
import sys
import logging

logger = logging.getLogger()
logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger.setLevel(logging.WARNING)

class Config:
    display_lines = 2
    display_width = 16
    display_length = display_lines * display_width

class Daemon:
    line = ""
    playing = False

    def __init__(self):
        self.ser = serial.Serial(sys.argv[1], 9600, timeout=1)

        # raise timeout to let arduino start up
        self.ser.timeout = 15
        incoming = self.readline()

        # arduino sometimes has a dirty buffer
        # and sends junk before the ready message
        if not incoming.endswith("ready"):
            sys.stderr.write("Got unexpected reply: "+incoming)
            raise Exception("Wrong client greeting")
        self.ser.timeout = 1

        self.client = mpd.MPDClient()
        self.client.connect("localhost", 6600)
        logger.debug("connected")

    def readline(self):
        return self.ser.readline().decode("ascii").strip()

    def update(self):
        old_line = self.line
        old_playing = self.playing
        changed = False

        incoming = self.client.currentsong()

        self.line = incoming["artist"] + " - " + incoming["title"]
        if self.line != old_line:
            changed = True

        self.playing = self.client.status()["state"] == "play"
        if self.playing != old_playing:
            changed = True

        return changed

    def run(self):
        self.update()

        last_display_update = 0.0
        last_song_update = 0.0
        now = 0.0
        time_diff = 0.0
        current_position = 0

        while 1:
            now = time.time()

            # check for incoming commands from arduino
            if self.ser.inWaiting() > 0:
                incoming = self.readline()

                logger.info("got serial command: '%s'", incoming)

                functions = {
                    "next": self.client.next,
                    "previous": self.client.previous,
                    "pause": self.client.pause,
                }
                try:
                    func = functions[incoming]
                    func()

                    # force redraw if anything changed
                    if self.update():
                        last_display_update = 0
                        current_position = 0

                except KeyError:
                    logger.warning("ignoring unknown serial command '%s'", incoming)
                    pass

            # check if song changed
            # TODO: use idle and poll (more often)?
            time_diff = now - last_song_update
            if time_diff > 2.5 or time_diff < -60.0:
                last_song_update = now
                if self.update():
                    last_display_update = 0
                    current_position = 0

            # update arduino display
            time_diff = now - last_display_update
            if time_diff > 0.8 or time_diff < -60.0:
                last_display_update = now
                if self.playing:
                    i = current_position

                    if i - 1 + Config.display_length == len(self.line):
                        i = 0

                    if i == 0:
                        # allow people to start reading
                        last_display_update += 2

                    if len(self.line) > Config.display_length:
                        buf = self.line[i:i+Config.display_length]
                    else:
                        buf = self.line.ljust(Config.display_length, " ")

                    i += 1
                    current_position = i
                else:
                    buf = "-- PAUSED --".center(Config.display_width, " ").ljust(Config.display_length, " ")

                    # always start at the beginning if we unpause
                    current_position = 0

                logger.debug("current_position = %s, buf = '%s'", str(current_position).rjust(3), buf)
                self.ser.write(bytes(buf, "ascii", "replace"))

            time.sleep(0.05)

if __name__ == '__main__':
    d = Daemon()
    d.run()