webXMPP

Check-in [f9faa86edd]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:First code-up, notifications onto a Ham repeater announcement
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:f9faa86eddf740d6213212a3c11a99558d723b1d6cee72d27032579273ca6e87
User & Date: vandys 2018-09-20 00:04:13
Context
2018-09-22
01:08
Bringup check-in: b40feebcbe user: vandys tags: master, trunk
2018-09-20
00:04
First code-up, notifications onto a Ham repeater announcement check-in: f9faa86edd user: vandys tags: master, trunk
2018-09-19
23:18
Snapshot, a notification client for DBus-ish mobile devices. check-in: 4a044ab760 user: vandys tags: master, trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added tools/ham.py.









































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#
# ham.py
#	Client for webXMPP server, ham radio oriented
#
# This daemon registers for notifications, then turns them into
#  a wav file (using flite) and keys up a transmitter and speaks them
#  over the air.
#
# In my case, it keys an actual repeater, which takes care of ID'ing.
#  Otherwise you'll need to change this to include your ID when
#  pushing out this telemetry.
#
import sys, time
from chore import pong, config
import pdb

# Initial condition, no events ever seen
Gen = 0

# Hook for logging
Debug = True
def log(s):
    if Debug:
	sys.stderr.write(s)
	sys.stderr.write('\n')

# Configuration, indented chore style
Cfg = None
def load_cfg(fn):
    global Cfg
    c = config.Config()

    # Notification server out on the Internet
    #
    # server <hostname>
    #  port X
    #  user name-on-server
    #  password password-for-name
    c.onearg.add( ("server",) )
    c.ints.add( ("server", "port") )
    c.onearg.add( ("server", "user") )
    c.onearg.add( ("server", "password") )

    # I/O line controller, probably local LAN
    #
    # rig <hostname>
    #  port Y
    #  channel Z
    #  audio "audio-device-name"
    c.args.add( ("rig",) )
    c.ints.add( ("rig", "port") )
    c.onearg.add( ("rig", "channel") )
    c.onearg.add( ("rig", "audio") )

    # Parse and set config dict
    Cfg = c.load_cfg(fn)

# New notifications contained in this packet
def notify(pak):
    global Gen, Debug

    inner = pak.inner
    log("Notification: gen %d -> %d\n" % (gen, inner["gen"]))
    for tup in inner["msgs"]:
        lt = len(tup)

        # Ignore mirrors of our own sends on other devices
        if lt and (not tup[0]):
            continue

        # No details at all, so just show one notification
        if lt in (0, 1):
            n1 = "New Message"
            n2 = None

        # Just who
        elif lt == 2:
            n1 = "New Message"
            n2 = tup[1]

        # Who plus headline 
        elif lt == 3:
            n1 = tup[1]
            n2 = tup[2]

        # Who plus headline plus body
        else:
            n1 = tup[1] + ": " + tup[2]
            n2 = tup[3]

	# Construct what to say
	pdb.set_trace()
	n = "Notification%s: %s" % \
	    ((" from %s" % (n2,)) if n2 else "", n1)
	adev = Cfg["rig"][1]["audio"][0]
	destfn = "/tmp/notify%d.wav" % (os.getpid(),)
	os.system("flite -t '%s' %s" % (n, destfn))

	# Key up the rig
	tx_on()
	time.sleep(1.0)
	# Say the message
	os.system("aplay -D '%s' %s" % (adev, destfn))
	time.sleep(0.5)
	tx_off()

# Endless execution, notification client
def run():
    global Cfg, Gen

    # Get a wrapper for our pong network connection
    pdb.set_trace()
    conn = pong.Client(cfg.server, cfg.port, cfg.user, cfg.password)

    # We need a send-receive-send pattern as a minimum to become
    #  an assured/streaming UDP "connection".  This loop starts
    #  a new socket, does a param get/got, and then a timed
    #  get.  At the timeout, the server provides a "got", after
    #  which we send another "get".  This keeps the firewall/NAT
    #  state alive indefinitely, at a cost of one transmit and
    #  one receive every 2.5 minutes.
    # When we hit a network error (usually due to change in our
    #  IP address), we reset the connection and start over with
    #  the param get/got.
    while True:
        # Params; we in particular need to know the server's
        #  intended timeout.
        while True:
            pak = conn.pingpong(conn.msg("params", "get"))
            if pak is not None:
                break
            # Note that on error, the "pong" library will already
            #  have closed out the socket
            time.sleep(pong.WAITNET)

        # This is how long they'll hold a notify/get pending before
        #  sending back a null result.
        # If we request one and don't hear back in this amount of
        #  time, we have a lost packet or something.
        # 1.1 is our 10% slop factor over the expected timeout
        #  from the server.
        tmo = pak.inner["timeout"] * 1.1

        # Server loop
        while True:
            # Always yield for a second, so no matter
            #  what we never CPU spin hard.
            time.sleep(1)

            # Next round of notifications
            # Request events starting at this serial number
            pak = conn.msg("notify", "get", {
                "gen": Gen, "detail": 3, "nmsg": 2})
            pak.who = dest
            resp = conn.ping_pong(pak, tmo)

            # Failure
            if resp is None:
                # We drop out of the server loop, and start over
                #  with a fresh socket and param get/got
                time.sleep(pong.WAITNET)
                break

            # Nothing happened
            if resp.inner["gen"] == gen:
                continue

            # New messages
            notify(resp)
            gen = resp.inner["gen"]

if __name__ == "__main__":

    # Usage
    if len(sys.argv) != 1:
	sys.stderr.write("Usage is: %s <config>\n" % (sys.argv[0],))

    # Config and run
    load_cfg(sys.argv[1])
    run()