Page 1 of 1

Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 8:01 am
by Swancoat
So, I'm not really looking for super specific code examples here or anyone to do the work for me. More like just a general ideas or tips on what methods I need to do something, or where to find examples to do the steps I need here. Of course I WOULD pay for a done solution... but this really is just a hobby for me, and so the amount I'm willing to pay is probably less than anyone would need to do the work!

Background:

I have an Airport Express hooked up to my whole home audio system, and I'd like to capture the metadata from when people stream sources to them so that I can display it on the keypads. There doesn't seem to be any way to do this with an Airport Express, so step one of my solution is Shairport-Sync (https://github.com/mikebrady/shairport-sync). Basically this is an app that runs on a Raspberry Pi that makes it appear as an Airport Express AND can export the metadata of whatever is playing. I've got this part working and it works fine. I have a Raspberry Pi up and running that is effectively an Airport Express. People streaming to it don't know the difference, and it's been bulletproof for a couple of months now.

Next: The metadata.

Shairplay-Sync pushes the metadata into a named pipe on the Raspberry Pi. I want to make a plugin that I can define the IP address of the Pi (and any other info I need to) and then read the metadata (mainly song title, album, artist) and do what I want with them (most simply I'll just store them in a device state, or user-definable variable, but I see Indigo Plugins can now publish/subscribe, so that'd be cool too).

What I need to know is how the heck I make my plugin read that data. Do I have to have something running on the Pi that sort of makes the file available? Or can I just have the plugin log on itself and get the data. I've sampled the data on the Pi and it's in XML, so an XML parser is going to be required to, but I can probably figure that out later.

What do I know? Not much. I know what a named pipe is. Barely. Pretty much what a bunch of googling can tell me (effectively a data 'stream', but defined like a file with a location, etc...?)

I've made a couple of plugins, but they're basically simple serial port interfaces. Open the port, read if something's there, parse it, update some variables. Take some input from the plugin and turn into a command and feed it to the serial port. Reading from a network socket seems a bit more complicated.

Any idea on how I need to go about this?

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 9:01 am
by racarter
I had a similar requirement (to access a file on the Pi) and went the FTP route. Install a barebones FTP server on the Pi (I used vsftpd) then you can get at your file using an FTP url such as:
Code: Select all
myContent = urllib2.urlopen("ftp://user:pass@%s/now.xml" % devIP).read(500)

where devIP is the Raspberry Pi IP address and now.xml is the file to be read. Not sure if this would work with a named pipe though...

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 3:34 pm
by kw123
I am using expect to send and get data with ssh and sftp to from the rpi
If anyone is interested I can post the code.


Sent from my iPhone using Tapatalk

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 6:30 pm
by DaveL17
kw123 wrote:
I am using expect to send and get data with ssh and sftp to from the rpi
If anyone is interested I can post the code.


Sent from my iPhone using Tapatalk

I suspect that 'expect' is actually the python requests module.

ETA: Not so much (see below). :D

Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 7:13 pm
by kw123
The Expect( I am using ) is its own language for terminal interactions based on tcl
And it comes preinstalled on each unix .. even on a mac

Sent from my iPhone using Tapatalk

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 7:42 pm
by DaveL17
kw123 wrote:
The Expect( I am using ) is its own language for terminal interactions based on tcl
And it comes preinstalled on each unix .. even on a mac

Sent from my iPhone using Tapatalk

Sorry, thought you were speaking Python! :D

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 7:55 pm
by kw123
there likely many expects .. :D

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 8:51 pm
by Swancoat
kw123 wrote:
I am using expect to send and get data with ssh and sftp to from the rpi
If anyone is interested I can post the code.


Sent from my iPhone using Tapatalk


Interested. Probably take me a while to decipher it, but I'd like to try! Thanks!

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Tue Sep 05, 2017 9:48 pm
by kw123
Will post tomorrow.


Sent from my iPhone using Tapatalk

Re: Plugin to read data from file on Raspberry Pi

PostPosted: Sat Sep 09, 2017 3:39 pm
by kw123
sorry took a bit longer:


this how it is called
in the shell you MUST use the full path to the executable (expect)
then the full path to the expect script and parameters each separated by a space userid password etc

Code: Select all
/usr/bin/expect  '/Library/Application Support/Perceptive Automation/Indigo 7/Plugins/uniFiAP.indigoPlugin/Contents/Server Plugin/dictLoop.exp' userID PASSWORD 192.168.1.x BZ.v xxxThisIsTheEndTokenxxx 40 10  /var/log/message


this is the expect code in file dictLoop.exp:
Code: Select all
set userID [lindex $argv 0 ]
set password [lindex $argv 1 ]
set ipNumber [lindex $argv 2 ]
set promptOnServer [lindex $argv 3]
set endDictToken [lindex $argv 4]
set readDictEverySeconds [lindex $argv 5]
set mcatimeout [lindex $argv 6]
set logfile [lindex $argv 7]

set timeout 10
spawn  ssh $userID@$ipNumber
expect {
    "(yes/no)? " {
        send "yes\n"
        sleep 0.1
        expect "assword: " { send "$password\n"}
    }
    "assword: " {
        send "$password\n"
    }
}
sleep 1.0;
expect "$promptOnServer" {puts "startxxxx\r";}
sleep 1.0

set timeout $mcatimeout
send "\r"
    for {set i 1} {$i >0} {incr i 1} {
        sleep 0.2
        expect "$promptOnServer"
        send "mca-dump | sed -e 's/^ *//' \r"
        expect "$promptOnServer"
        puts "$endDictToken$i\r\r"
        sleep $readDictEverySeconds
        send "\r"
        expect "$promptOnServer"
        if {$logfile != "doNotSendAliveMessage"} {
            send "echo ThisIsTheAliveTestFromUnifiToPlugin >> $logfile\r"
        }

    }

expect "$promptOnServer"
puts "end\r"
send "exit\r"
sleep 0.1
expect eof


it logs into a unifi AP
then it runs mca-dump .. removes leading space (it produces a heavily formatted json dump. with sed -e 's/^ *//' the leading space get removed)
adds the string "xxxThisIsTheEndTokenxxx" to the output
and adds "ThisIsTheAliveTestFromUnifiToPlugin" to the uniform AP log file

and does that in a loop

then the plugin
with :
Code: Select all
 
                    cmd = "/usr/bin/expect  '" + \
                          self.pathToPlugin + self.expectCmdFile[type] + "' " + \
                          self.unifiUserID + " " + \
                          self.unifiPassWd + " " + \
                          ipNumber + " " + \
                          self.promptOnServer[type] + " " + \
                          self.endDictToken[type]+ " " + \
                          str(self.readDictEverySeconds[TT])+ " " + \
                          str(self.timeoutDICT)
                    if type.find("AP") >-1:
                         cmd += "  /var/log/messages"
                    else:
                         cmd += "  doNotSendAliveMessage"
               ListenProcess = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
                ##pid = ListenProcess.pid
                ##self.ML.myLog("all", " pid= " + str(pid) )
                msg = unicode(ListenProcess.stderr)
                if msg.find("[Err ") > -1:  # try this again
                    self.ML.myLog("all", type + " error connecting " + msg, type=u"EXPECT")
                    self.sleep(20)
                    continue
                flags = fcntl.fcntl(ListenProcess.stdout, fcntl.F_GETFL)  # get current p.stdout flags
                fcntl.fcntl(ListenProcess.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
                return ListenProcess, ""

receives these dumps every xx seconds through the pipe opened.

hope that is not to complex ..


In a nutshell:

expect sends characters to ssh which can be controlled by the return messages from ssh with If s etc.

then you can start processes on the remote computer, like create dump-file or open a pipe (e.g. tail-f /var/log/syslog) and feed that to a listener process in your plugin.

looks complicated and it is not for beginners ..

a simple expect script should be easy. there are many examples on the web.

Karl