Beginner questions

General discussions about Automagic and automation in general

Moderator: Martin

robchoc
Posts: 81
Joined: 20 Jun 2018 12:38

Beginner questions

Post by robchoc » 22 Sep 2018 18:52

I'm going to be creating lots of simple flows just to get my head around how things work.

I'm writing a very simple flow to ask a user to pick a number from 1-5 and then display the result.

The first obstacle is how/where do I specify what variable name to use for my input dialog?

Thanks

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Beginner questions

Post by Desmanto » 22 Sep 2018 19:30

You don't need any variable, but you can if you want. Just use Action Input Dialog, choose the type that you want. If only one choice allowed, then Single Choice Menu is preferred. At the List Values, simply use : 1,2,3,4,5
Automagic always handle the list value in comma delimited format. You can also put something like : apple,banana,cherry,dewberry,eggfruit

To show the result, you can use the common one, action Notification on Screen; it is equivalent to the "Flash" in tasker, which is to show toast message. Or you can use one of the most powerful feature of automagic, condition Debug Dialog.

To handle list using variable, you can put them into variable first. Use script, and create the list.

Code: Select all

choice = newList(1, 2, 3, 4, 5);
choicefruit = newList("apple", "banana", "cherry", "dewberry", "eggfruit");
Create a input dialog after this script. Then to use them in the input dialog, at the List Values field, simply tap the ellipsis, you can choose the {choice,listformat,comma}.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

robchoc
Posts: 81
Joined: 20 Jun 2018 12:38

Re: Beginner questions

Post by robchoc » 23 Sep 2018 19:03

That's very helpful.

My next question/project is a little more complicated and was done in Tasker and I'm trying to convert it to work in Automagic.
It basically does an HTTP get request and I then splits the variable to get my needed text.

First part is a simple HTTP get request and I now have the website data in response.

Now I need to split the data by "<span class="pretty-sum">" and then further split by "<"

In Tasker I was able to see a list of variables that were created in the VARS tab and then select the one I wanted.

In Tasker I used Variable split <span class="pretty-sum">
And then I split again with <
And I was then left with the correct text in a variable that I could see in the VARS tab.

Look forward to your reply.

Thanks

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Beginner questions

Post by Desmanto » 24 Sep 2018 05:11

Parsing data is one of my favourite topic in Automagic. Here I will just assume you want to retrieve some data from weather info. And then using HTTP request, will store the result in {response}. It should be longer, but let assume the response you get only like this.

Code: Select all

response = '<span class="pretty-sum">Berlin, Germany | 21 C; | Clear</span>';
You want the text inside the span tag : Berlin, Germany | 21 C; | Clear

Parsing using split()
You can simulate the response just in script. So we assign the html we want to parse to {response}. Then do the splitting as you have done in tasker.

Code: Select all

response = '<span class="pretty-sum">Berlin, Germany | 21 C; | Clear</span>';

a = split(response, '<span class="pretty-sum">');
b = split(a[1], '</span>');
text = b[0];
When first split, {a} will contain 2 elements, '' (blank) and Berlin, Germany | 21 C; | Clear</span>. We only need to access second element, and automagic list start the index from 0. So second element is index 1. Hence to access the next split, we use a[1]. (2nd element in list {a}). After splitting the b, it will also result in 2 elements, and we access only the first one, so b[0]. {text} will contain Berlin, Germany | 21 C; | Clear.

Put a condition debug dialog after the script to see the value of each variables.

Accessing variable
To access any available variable, tap the variable button in script or expression. Or at any element editing, tap 3 dot menu > select variable. For list/map type variable, you only can access the parent (list/map), not the child (list/map element). In this case, you can select {a}, but not the {a[0]}. You have to type the [0] by yourself. a and {a} is the same. Accessing variable usually require the braces {}. But when using it in script, expression, Control UI, extras, {} is not needed. I purposely put {a} in above example to make it clear that it is variable {a}.


Problem with split()
However, when parsing data like this, split() is not recommended, you will ended splitting so many times and the structure becomes uglier each time you need extra split. From the example above, if I want to split again, the city, country, temperature and forecast, I need to do another 2 split(). Continue from our script above.

Code: Select all

response = '<span class="pretty-sum">Berlin, Germany | 21 C; | Clear</span>';

a = split(response, '<span class="pretty-sum">');
b = split(a[1], '</span>'); // b[0] = Berlin, Germany | 21 C; | Clear

c = split(b[0], ",");
city = c[0]; // Berlin

d = split(c[1], " \\| "); // | is special char, need to double escaped using \\
country = d[0]; // Germany
temp = d[1]; // 21 C;
forecast = d[2]; // Clear
As you can see, we have to split so many times and each additional split make it more confusing. As you need more and more temporary variable to hold the data.

Better function, findAll()
Here comes the better function, use findAll(), which utilize regex parsing. The same data parsing can be done just in one-hit KO.

Code: Select all

response = '<span class="pretty-sum">Berlin, Germany | 21 C; | Clear</span>';

find = findAll(response, '<span class="pretty-sum">(.*), (.*) \\| (.*); \\| (.*)</span>', true);
city = find[0][1]; // Berlin
country = find[0][2]; // Germany
temp = find[0][3]; // 21 C
forecast = find[0][4]; // Clear
A single findAll() will automagically group all needed data in a list. The findAll() with group matching (true) will result in nested list. That's why we need extra [0] to access the first match, then another [1] to access the first group capture, city = find[0][1]. use [2], [3], [4] to access the subsequent variable. Regex parsing also much more precise. You can see the {temp} value by splitting result in 21 C; (there is additional semicolon). While using findAll(), we can exclude the ; directly, result in more precise matching, 21 C

Regex
To learn about regex, how to use the syntax or the grouping, I suggest to visit regexr.com. Automagic also has built-in regex tester that you can access from action script or condition expression. You can see more example of parsing data at my index, section HTTP request, Parsing data and Regex pattern.

Tasker also can do regex parsing, but require additional plugin in AutoTools (need to install the app). And the parsing also have to be done in each action, can't be done just in single script action with several lines. In short, sometimes you will need 3-8 actions in tasker, but you only need single script with several lines of code in Automagic. You got better overview of your variables in Automagic.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

robchoc
Posts: 81
Joined: 20 Jun 2018 12:38

Re: Beginner questions

Post by robchoc » 24 Sep 2018 18:23

Really appreciate the comprehensive answers as they are teaching me more than I'm asking which is really helpful in learning how to use Automagic.

The first bit I have working perfectly now and I used the findAll, not sure if to call it a function or command.

The next part of my script involves using a shell script in Tasker with the following:

curl -H "Content-Type: application/json" -X POST -u "m0tff:Password" -d "{ \"text\":\"%Message\", \"callSignNames\": [\"m0tff\"], \"transmitterGroupNames\": [\"all\"], \"emergency\": false }" http://hampager.de/api/calls

I changed %Message to {pounds} in the Automagic script.

I tried this in the script action in Automagic but it keeps saying I need lots of ";" and an expected expression.

Any help appreciated.

So what my flow will do once complete is to get the exchange rate from 2USD to GBP and then send it to my pager every hour between 9:00 - 21:00

Looking forward to your reply.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Beginner questions

Post by Desmanto » 25 Sep 2018 16:55

findAll() is function inside script/expression.
You can't use curl inside script. Automagic script is a separate programming language on its own, not a usual scripting.

How do you invoke the curl from tasker? Do you place the curl inside certain folder or have you put the curl into your environment path (/system/bin or xbin)? The equivalent of the shell script in automagic is "Execute Command" or "Execute root command" (if need root).

Tasker user % to denotes variable, while automagic use {}. So you have to put {Message}. And if you need global variable, tasker user capital. But automagic use the prefix global_, capital letter doesn't matter. So you can use {global_message}.
After you change the %, you can use the exact same command in the execute command. The result is stored in stdout, which you can passed to another action.


Automagic has built-in HTTP request. Basically what you want to do above using curl, can be done using the built-in action. (POST, json) But AFAIK, it doesn't support the modern username password authentication. I never successfully using the modern authentication to call certain web API and until now I still don't need it. (Although I just finish another flow with 3 HTTP request and regex parsing). Some of my other flows to login to (2 different) wifi router, only require the username password to be passed in Form Field List.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

robchoc
Posts: 81
Joined: 20 Jun 2018 12:38

Re: Beginner questions

Post by robchoc » 25 Sep 2018 18:23

Thanks for getting back to me.

In Tasker, I just copy the script into the Run Shell action and it works.

Curl is built into some versions of Android from what I can see. For example, on my Android 7.1.1, my Tasker curl script works but on my other phone that is Android version 7.0 the curl script does not work. Both devices are unrooted.

I copied the curl script into Execute Command but it still does not work and I get the following:

{
"code": 4003,
"name": "Invalid Json",
"message": "Expected \u0027:\u0027 at line 1 column 8 path $.error"
}

Thanks for the help.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Beginner questions

Post by Desmanto » 26 Sep 2018 11:02

Oh, I forgot that you have curly braces at the outer part of the json. Those will be interpreted by automagic as variable, messing up many things. The expected \u0027 is single apostrophe, which probably comes from your {message}

It seems you have to declare the json outside, to make sure it is properly parsed. Use script before the execute command. Put this

Code: Select all

message = "put your message here";

js = newMapFromValues(
"text", message,
"callSignNames", "m0tff",
"transmitterGroupNames", "all",
"emergency", false);

command = 'curl -H "Content-Type: application/json" -X POST -u "m0tff:Password" -d ' + toJSON(js, false);
Message is where you put the message. js is the json need to be passed, we create it in a Map object first before converting it to json. The last is to combine the curl and other parameter to json. Use {command} in the execute command.

I am using custom ROM RR 6.2 Oreo 8.1, and I have curl. I thought it was custom ROM implementation. Because on previous LP 5.1 I don't have it. So it seems to be built-in binary already in newer android version.

If the curl is working, you might want to try the native built-in solution from automagic, HTTP request.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

robchoc
Posts: 81
Joined: 20 Jun 2018 12:38

Re: Beginner questions

Post by robchoc » 26 Sep 2018 14:39

Where does the URL go?

This is what is now contained in js:
{text=1.52, callSignNames=m0tff, transmitterGroupNames=all, emergency=false}

That looks perfect but not sure where the error is in the last part in the execute command.

This is what I get back in stderr:
sh: syntax error: '(' unexpected

I will try the HTTP request see if I can get that working as it would be more useful to me as I don't have curl on my main phone and would like to get this flow working on there.

Appreciate all the help.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Beginner questions

Post by Desmanto » 26 Sep 2018 15:48

Ooops, sorry. I forgot the url. I thought it is HTTP request. My bad. This should be script, append the url at the command.

Code: Select all

message = "1.52";

js = newMapFromValues(
"text", message,
"callSignNames", "m0tff",
"transmitterGroupNames", "all",
"emergency", false);

command = 'curl -H "Content-Type: application/json" -X POST -u "m0tff:Password" -d ' + toJSON(js, false) + ' http://hampager.de/api/calls';
You can try to put a condition debug dialog and check the value of the command, it should be

Code: Select all

curl -H "Content-Type: application/json" -X POST -u "m0tff:Password" -d {"text":"1.52","callSignNames":"m0tff","transmitterGroupNames":"all","emergency":false} http://hampager.de/api/calls
There should be no bracket in the command, as we don't put any () at there (except if you quote the toJSON function)

If you don't have curl, you can download the binary for android at the website : https://curl.haxx.se/gknw.net/7.40.0/di ... oid.tar.gz
Extract it and put at certain folder. But I don't if at android Nougat above, still allow the curl binary to be executed from sdcard. In Oreo, it is not allowed already, I have to used root and push the binary to any where in the /data or /system. We can still push the binary to /data/local/tmp without root, but I doubt we can execute directly from there.


To use HTTP request, it is almost the same as the curl. But I never success with the username password. You can try this if you want.
Action HTTP Request
URL : http://hampager.de/api/calls
Verify Certificates : Uncheck
Authentication : check Basic Authentication
Username : m0tff
Password : yourpassword
Request Method : POST
Content Type : General Text - application/json
Data : {toJSON(js, false)}

If Content Type General Text failed, you can try with Form and Form Data file Upload. But probably you have to change it to the direct value
Content Type : Form - (application/x-www-form-urlencoded)
Form Field List : text=1.52,callSignNames=m0tff,transmitterGroupNames=all,emergency=false


I don't know if the username password method above work or not. As my other flows, i usually pass the username password in the Content Type Form (the router has additional token)
Content Type : Form - (application/x-www-form-urlencoded)
Form Field List : frashnum="",Frm_Logintoken={token},Username=admin,Password={pass}

So you probably need to disable the basic authentication and change yours to Username=m0tff,Password=yourpassword,text=1.52,callSignNames=m0tff,transmitterGroupNames=all,emergency=false
But need to make sure it works first with curl.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

Post Reply