Is there a better way to do this? (a bit long of a read)

If you have a question or need help, this is the place to be.
cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Is there a better way to do this? (a bit long of a read)

Post by cvben » Sun Dec 31, 2017 4:05 am

Again a bit long winded, but here it goes...

EG 0.5.0-rc4
Windows 10

Basically I am using Autovoice (see android/tasker) to send my pc commands through Join (new Autoremote if anyone knows either). Join is just a bridge that sends variables to the pc which triggers handy little events in Eventghost.

My end goal is to have a structured command process to accomplish specific goals.

So if I want to open a specific station on pandora for instance I'll send a command such as "play rock radio"

Just in case this is relevant I will give the simple regex filter I am toying with:

Code: Select all

(?<command>play) (?<genre>.+) radio
which filters the command type (here just play) and the genre (which basically matches the word before radio) this is then sent to EG in the format:

Code: Select all

Autoremote.Message.play,genre
My thought here is to simply use the first bit (play) to trigger an EG profile and insert the second value (genre) as needed in some script or further event triggers

the problem here is that comma (Autoremote.Message.play*--->>,<<---*genre)

EG reads it as one payload so If I want to parse it var[0] = p, var[1] = l, var[3] = a, var[4] = y etc. all the way to the end of the genre that is passed, which is not very convenient.

My solution below:

Macro 1

1) an event listener for something like "Autoremote.Message.play,genre" mentioned above

2) Python Script

Code: Select all

aa = eg.event.payload.arpar
# arpar refers to the payload passed by Join/Autoremote

bb = ''.join(aa)
#make the array a string

cc = bb.split(',')
#split on the comma

eg.TriggerEvent(cc[0])
#triggers text before comma as an event, this theoretically helps with chaining or filtering in various macros I might create. Throws the event "Main.Play" in this instance 

eg.globals.genre_a = cc[1]
#stores the genre in a variable to be used in a moment
Macro 2

1) Event listener for "Main.Play" mentioned above

2) again python script/statement

Code: Select all

eg.TriggerEvent(eg.globals.genre_a)
#triggers the above stored var as "Main.Genre"
my reasoning here is that first I need a quantifier of what exactly I want to do with the received variable, which is accomplished in macro 1. So by giving a voice command to my phone or Google home I can have many such commands (ex play, open, find, create, delete, transcribe, etc) or set different filter patterns which only send a command when the pattern is matched and filter/evaluate from there if need be.

the separation into separate macros makes sense to me for this use case, because I want the "content" to be evaluated after the "quantifier". For example if I give it the "play" command with another filter of "radio" it might open a specific spotify/pandora page.

I'm sure I could of course set up a dictionary here, but I think natural language usage would be nice, I usually say "play x movie", but also say "play y song." Or open for various things (files, movies, songs).

This works at the moment, but I was just wondering if I took needless steps, or there was a "cleaner" way to accomplish this. Also sharing is fun, so if you use tasker/autovoice/autoremote knock your self out with my terrible code

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Sun Dec 31, 2017 6:31 am

first question.

in a code example you use

Code: Select all

bb = ''.join(aa)
#make the array a string

cc = bb.split(',')
#split on the comma
you are using the word array. so I am guessing this is either a list or a tuple based on what you are doing in the code.
what is the exact contents of eg.event.payload.arpar
actually what is the contents of eg.event.payload

adding a simple print eg.event.payload at the top of the script will print it out in the log.

If you can walk me through a real example of the data that is being passed it would help me to understand better what is going on.
it seems as tho you have Autovoice doing some of the parsing of the data. can it be set up so that Autovoice simply sends the whole command to EG?
If you like the work I have been doing then feel free to Image

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Sun Dec 31, 2017 6:08 pm

I actually thought about this very question being asked, then I forgot

So much for trying to be thorough.. :)

Image

Above is what you are looking for. From what I know of the process is that the overarching message is ideally sent as a payload "armessage" referring to the older version of this which used AutoRemote. Then the "arpar" payloads make up the message.


Also I can indeed send the whole command to EG without the regex filter on that end. I just figured I'd be doing the same filter on EG's end and it didn't matter, but I guess you are asking because there is hopefully some way to parse the regex on Eg's end and save myself some trouble....


Edit: It dawns on me to do something like so:

Message triggers as "AutoRemote.Message.play rock radio"

Code: Select all

import re

some_var = eg.event.payload.arcomm
# perhaps might have to use 'armessage', or 'arpar' vars here
The second part of this is where I might need some help as I am learning Regex, but I think:

Code: Select all

phrase_eval = re.search('(?<command>play) (?<genre>.+) radio')', same_var)
is hopefully getting in the right direction

then I need to trigger events I can do so afterwards

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Sun Dec 31, 2017 8:10 pm

As I feared my regex is lacking and I've hit a dead end

I get: SyntaxError: EOL while scanning string literal (21, line 3)

from code:

Code: Select all

import re

phrase_eval = re.search('(?<command>play)(?<genre>.+) radio')', eg.event.payload.armessage)

print(phrase_eval)

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 12:49 am

when dealing with regex it is not a constant like python or c++ meaning there are different flavors of it. and each flavor of regex has variations in punctuation and syntax.

But if we are looking for simple things in the line of code there is no need to use such a hefty tool like regex. I also wanted to know about doing the parsing of the data off of the mobile device because any over head saved on it is better performance of the mobile device. and a pc has far more computing power then a mobile device does so the hit in resources would be far less noticeable. i know i am speaking in micro seconds here but that time does add up. what would take a pc say a second to do would take a mobile device 10 seconds to do.


i am curious.

put the following code into a script that has one of the events from you device. and cause the event to take place.
paste the results that are printed into your log here.

Code: Select all

print eg.event.payload
print dir(eg.event.payload)
print eg.event.payload.__dict__
I am trying to get a better understanding of the layout of what eg.event.payload is. There could be some small treat hidden somewhere that could make this an easier task.

also if this message structure of play rock radio the standard you are using?

as an example
play movie title tv
play song name radio

is there a list of genres?


does your system currently match up movie/music titles? if not an example is below. it is pseudo code but it gives the basic idea.

obtaining a list of available song\movie titles is easy this can be done using fnmatch and os.listdir

Code: Select all


movie_name = 'Some Movie Title'
movies = os.listdir(r'c:\movies')

for movie in movies:
	found_movie = fnmatch.fnmatch(movie, movie_name + '*')
	index = len(movie_name)
	while not found_movie:
		index -= 1
		if index <= len(movie_name) / 2:
			break
		found_movie = fnmatch.fnmatch(movie, movie_name[:index] + '*')
	if found_movie:
		break
else:
    eg.StopMacro()
    eg.Exit()

result = movie

This can be used to take the spoken command that is passed to EG in the form of a title and will play it.
how this works is fnmatch allows for use of wildcards the "*" being match anything that starts with the text before it (in our case)
so we iterate through all of the filenames then start chopping off the end of the spoken title up to 50% (this is for errors in speech to text).
if it doesn't make a match it stops the macro and exits the script.
By setting the found movie to result it actually sets it to eg.result which can then be used by another action.
If you like the work I have been doing then feel free to Image

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Mon Jan 01, 2018 1:29 am

Result from the first bit of script you posted:

Code: Select all

 ['GetDescription', '__doc__', '__init__', '__module__', '__repr__', '__str__', 'arcomm', 'armessage', 'arpar', 'files', 'messageObj', 'plugin', 'sender']
         Traceback (most recent call last):
           Python script "22", line 3, in <module>
             print eg.event.payload.__dict__
           File "C:\Program Files (x86)\EventGhost\plugins\AutoRemote\__init__.py", line 717, in __repr__
             return self.__str__()
           File "C:\Program Files (x86)\EventGhost\plugins\AutoRemote\__init__.py", line 711, in __str__
             for key, value in params.items():
         AttributeError: 'str' object has no attribute 'items'
Link to the creator's page : https://joaoapps.com/autoremote/eventghost/

Link to the __init__ pluin: https://www.dropbox.com/s/3o0f18xzwgk0y ... __.py?dl=1

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Mon Jan 01, 2018 1:33 am

I'll have a look at your 2nd example a bit more tomorrow, I really appreciate the help and the work you put in.

Edit: Actually I took the few seconds reading it actually required and I like it. Good stuff

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 5:04 am

ok so we do have a response even tho it threw an error. so now we know that eg.event.payload has some more tidbits of information we might be able to use. run the code below and lets see what the output is.

Code: Select all

print 'arcomm:', eg.event.payload.arcomm
print 'armessage:', eg.event.payload.armessage
print 'arpar:', eg.event.payload.arpar
print 'files:', eg.event.payload.files
print 'messageObj:', eg.event.payload.messageObj
print 'plugin:', eg.event.payload.plugin
print 'sender:', eg.event.payload.sender

try:
	print 'GetDescription:', eg.event.payload.GetDescription()
except:
	print 'GetDescription:', eg.event.payload.GetDescription

try:
	print 'dir messageObj:', dir(eg.event.payload.messageObj)
except:
	print 'dir messageObj: Failed'
If you like the work I have been doing then feel free to Image

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Mon Jan 01, 2018 6:47 am

Code: Select all

eg.event.payload.armessage: u'play test radio'
eg.event.payload.arpar: [u'play', u'test', u'radio']
AutoRemote.Message.play u'play test radio'
   Python Script
      Python Script
         arcomm: 
         armessage: play test radio
         arpar: [u'play', u'test', u'radio']
         files: []
         messageObj: Traceback (most recent call last):
           Python script "8", line 5, in <module>
             print 'messageObj:', eg.event.payload.messageObj
           File "C:\Program Files (x86)\EventGhost\plugins\AutoRemote\__init__.py", line 711, in __str__
             for key, value in params.items():
         AttributeError: 'str' object has no attribute 'items'
My guess on the unreturned values are that they are placeholders for potential attributes sent on the end of Join(autoremote)

eg.event.payload.files - submenu offers attaching files for transfer
eg.event.payload.messageObj -submenu offers to send a popup like message to the pc/phone as a title and text or variables as text
eg.event.payload.plugin - a broader handler, submenu offers making device play a ring sound at max volume, set wallpaper, take screenshots, request location, send clipboard, and display n alternate form of a message in the form of a "bubble" (see facebook chatheads)
eg.event.payload.sender - seems logical to refer to the sending device or payload containing device information/ possible api info

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 7:00 am

ok cool. it seems as tho this would be the one to use.

eg.event.payload.arpar: [u'play', u'test', u'radio']

can you run the same script except using a multi word genre for me please?. I am wondering if it is going to make a list simply split by spaces.
If you like the work I have been doing then feel free to Image

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Mon Jan 01, 2018 7:08 am

I ran two tests here:

1st

tried the same script with the "print 'messageObj:', eg.event.payload.messageObj" commented out since it threw the exception

Code: Select all

    Python Script
         arcomm: 
         armessage: play test radio
         arpar: [u'play', u'test', u'radio']
         files: []
         plugin: <eg.CorePluginModule.AutoRemote.AutoRemote object at 0x0744CF70>
         sender: None
         GetDescription: 
         eg.event.payload.armessage: u'play test radio'
         eg.event.payload.arpar: [u'play', u'test', u'radio']
         dir messageObj: ['DoBeforeSend', 'FromDict', 'FromJson', 'FromJsonString', 'GetCommunicationType', 'GetHttpEndpoint', 'GetParams', 'GetParamsGCM', 'Send', 'SendFiles', 'SendSync', 'ToJson', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'collapseKey', 'communication_base_params', 'downloadFile', 'executeRequest', 'files', 'key', 'message', 'password', 'sender', 'target', 'ttl', 'version']
next per your above suggestion:

Code: Select all

      Python Script
         arcomm: 
         armessage: play rock and roll music radio
         arpar: [u'play', u'rock', u'and', u'roll', u'music', u'radio']
         files: []
         plugin: <eg.CorePluginModule.AutoRemote.AutoRemote object at 0x0744CF70>
         sender: None
         GetDescription: 
         eg.event.payload.armessage: u'play rock and roll music radio'
         eg.event.payload.arpar: [u'play', u'rock', u'and', u'roll', u'music', u'radio']
         dir messageObj: ['DoBeforeSend', 'FromDict', 'FromJson', 'FromJsonString', 'GetCommunicationType', 'GetHttpEndpoint', 'GetParams', 'GetParamsGCM', 'Send', 'SendFiles', 'SendSync', 'ToJson', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'collapseKey', 'communication_base_params', 'downloadFile', 'executeRequest', 'files', 'key', 'message', 'password', 'sender', 'target', 'ttl', 'version']

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 7:21 am

I am also going to need a breakdown of the syntax for a command.

i know you have the example of "play genre radio" and you mentioned there are other play commands that would be for music files or videos. can you give me an example of these commands..

I know this is kind of a pain but a list of everything you want to be able to do, what the commands would be to do a specific task.

I am thinking you might want to change the syntax a little to make it easier so the command "play" would not have to be used more then once with a specific identifier as the trailing bit.


as an example.

"movie Bill and Ted's Excellent Adventure" would play a movie (file).
"song Aerosmith Sweet Emotion" would play an MP3 (file)
"album Aerosmith Greatest Hits" would queue a whole album (file)
"radio Electronica" launch pandora and play that specific station
"radio Aerosmith" same as above

and you can add the basic commands of:

mute on
mute off
pause
stop
play
volume up
volume down
volume 30 (direct input)

and we can easily store what the last starting command was so if you use a different starting command check and see if something is playing from a different app and stop it (if not already stopped) and then start the new playback.

and the sub commands or second set of commands would only function if something was playing. (except play, the case with this is if it is paused then to play it)

then you can add "queue song AeroSmith Dude Looks Like a Lady" and it would queue a new song if the major command was "song" and album for album. you would not need an identifier if it was for a movie and if a command was out of syntax for the major command then display some kind of an error.

this would be far easier to parse then having a command at the beginning that can be a duplicate and add an identifier to the end to be able to identify what "play" command it is.

you could use "change station some station name" for pandora.


this is simply a suggestion.
If you like the work I have been doing then feel free to Image

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 7:33 am

also. to do any of the file parsing like i gave in the example a couple of posts back we can also add in better hits by replacing any spaces with "?" as i do know some of the downloaded files can either have a "." or a "_" in place of the space. we can also add a * to the front due to encoder identification. most times you don't have all of your titles packed into one folder. there is some kind of structure to it. example would be a movie title would be the folder name and in that folder would be things like a sample. and a subtitle file, possibly album art. all we do for this case is grab the largest of the files in the folder. and if you use varying players based on what the encoding type is that can be handled as well.

and via the use of enables and disables based on what player is running we would be able to select which macros become active for the minor commands. pause, stop. things like that.
If you like the work I have been doing then feel free to Image

User avatar
kgschlosser
Site Admin
Posts: 5498
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: Is there a better way to do this? (a bit long of a read)

Post by kgschlosser » Mon Jan 01, 2018 7:38 am

but if you were to use your original syntax.. then you would use the following example for best parsing results

Code: Select all

data = eg.event.payload.arpar

command = data[:1]
app = data[-1]
program = ' '.join(data[1:-1])

print 'command:', command
print 'app:', app
print 'program:', program
If you like the work I have been doing then feel free to Image

cvben
Posts: 38
Joined: Wed Jun 07, 2017 8:30 pm

Re: Is there a better way to do this? (a bit long of a read)

Post by cvben » Mon Jan 01, 2018 8:08 am

First, you are the man for some great suggestions! I tend to overthink and underplan. Starting off ignorant and hopefully finding a "working" solution by beating my head against something very thick. Thick wall, thicker skull.... :D

I'll put some actual work in instead of thinking about it and report back. New years resolutions begin.


!!Edit: Above code is exactly what I was trying to do!

I'll keep some of these snippets to remind me to to over-engineer a solution to a seemingly simple problem. Great stuff, again thank you.

Post Reply