Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Sending commands from Android+Tasker to QM
#1
Function dialog_HTTP_server_for_Tasker
Code:
Copy      Help
;This is a simple HTTP server that can receive and return data.
;Can be used for example to launch QM macros from other computers and smartphones.
;Below is instructions how to send commands from an Android mobile device with Tasker.

;On your Android device install Tasker app. It is for sending HTTP requests. Costs $2.99. Or find a free app that can send HTTP requests.
;In Tasker create new task and add action HTTP Post.
;;;Set field Server:Port, like 192.168.8.123:5033. As Server, use one of IP addresses that you can see in QM Options -> Network. The 5033 is server's port that is used in this macro, see below.
;;;Set field Data / File. Can be any text or variable or file. At first just for testing.
;;;Set field Content Type, like text/plain.
;;;Optionally set field Output File, like Documents/test1.txt. Only if you want to receive some data from QM.

;Run this function and in the dialog click button Start.
;Run the Tasker task.
;;;In QM output you should see Tasker's HTTP POST request that includes data at the end.
;;;If was set Output File, the task creates the file. It contains text DATA.


if(getopt(nthreads)>1) ret ;;allow single instance
#compile "__TcpSocket"

str dd=
;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 230 116 "TCP server"
;4 Static 0x54000000 0x0 170 8 22 12 "Port"
;5 Edit 0x54032000 0x200 196 6 32 14 "port"
;6 Button 0x54032000 0x0 6 6 48 14 "Start"
;7 Button 0x5C032000 0x0 58 6 48 14 "Stop"
;14 Static 0x54000000 0x0 6 30 36 12 "Response"
;15 Edit 0x54231044 0x200 46 28 182 85 "res"
;END DIALOG
;DIALOG EDITOR: "" 0x2030300 "*" "" ""

str controls = "5 15"
str e5por e15res
e5por=5033

;minimal HTTP response with no data.
;e15res="HTTP/1.1 200 OK[][]"

;HTTP response with some data.
e15res="HTTP/1.1 200 OK[]Content-Type: text/plain[]Content-Length: 4[][]DATA"

if(!ShowDialog(dd &sub.DlgProc &controls 0 0 0 0 0 0 100)) ret


#sub DlgProc
function# hDlg message wParam lParam
TcpSocket- t_server
sel message
,case WM_INITDIALOG
,case WM_DESTROY
,t_server.Close
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case 6 ;;Start
,t_server.ServerStart(GetDlgItemInt(hDlg 5 0 0) &sub.OnClientConnected hDlg 1)
,EnableWindow lParam 0
,EnableWindow id(7 hDlg) 1
,
,case 7 ;;Stop
,t_server.Close
,EnableWindow lParam 0
,EnableWindow id(6 hDlg) 1
ret 1

err+ out _error.description


#sub OnClientConnected
function TcpSocket&client $clientIp hDlg !*reserved

;This function is called in server side, when a client connects.
;This function runs in separate thread for each client connection.

out F"<><z 0xc0ffff>SERVER: client connected:</z> {clientIp}"

str s
client.Receive(s 1000)
out F"<><Z 0xc0ffff>SERVER: client request:</Z>"
out s

s.getwintext(id(15 hDlg))
client.Send(s)

err+ out _error.description


Attached Files
.qml   TcpSocket.qml (Size: 19 KB / Downloads: 563)
#2
This works perfectly!!!!!!
THANK YOU!!!

Just to re-iterate like stated in the green comment, make sure:
  • You actually press "start" in the dialog
  • Within in QM in the menu above: TOOLS > OPTIONS > tab: NETWORK , you see your machines IP. Use this IP in TASKER
  • The port you enter in tasker matches the port you enter this (above) dialog
  • Make sure your ROUTER and WINDOWS-Firewall doesn't block the port you entered. (above dialog and tasker)
  • Set "Content Type" in tasker to: text/plain

This is amazing, thank you again for putting time in this!!!
Android and windows using QM can communicate with each other!!!
#3
Thank you both Gintaras for the wonderful HTTP Server Dialog to receive input from Tasker (android device) and Ron for the nitty gritty details in setting up the connection with tasker within QM app.

I'm looking forward to seeing more examples on this topic like:
- Sending image/file/audio/video from PC/Laptop to Android machine and vice versa.
- Activate/run an app in android phone by using QM on PC/Laptop and vice versa.
#4
I am using the POST method with Form-Data to submit some binary files and encountered the following issue:

Test file(Pic.png) size is 1342KB; file(Doc.docx) size is 525KB
 
Code:
Copy      Help
client.Receive(_s 2048000); out F"SERVER: client request:[]{_s}"
 
Using the above parameters: The response speed is very fast, but The data reception is unstable, sometimes complete and sometimes incomplete.
 
Code:
Copy      Help
client.Receive(_s 0); out F"SERVER: client request:[]{_s}"
Using the above parameters: The response speed is too slow, but The received data is always complete, the response speed is still very slow even when testing locally.

I have been stuck on this issue for a few days. Thanks for Any suggestions or help!


_____________________________________________________________The incomplete returned data is as follows:
POST /saveFile HTTP/1.1
Accept: application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7
Content-Type: multipart/form-data; boundary=--------------------------493891244813812906191617
content-length: 1831226
User-Agent: axios/1.7.4
Accept-Encoding: gzip, compress, deflate, br
Host: 127.0.0.1:5032
Connection: keep-alive

----------------------------493891244813812906191617
Content-Disposition: form-data; name="FileBin"

iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAAEAAE..............(The data is truncated in the middle, and there is no ending line similar to the red text shown below.
)
_____________________________________________________________The complete returned data is as follows:

POST /saveFile HTTP/1.1
Accept: application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7
Content-Type: multipart/form-data; boundary=--------------------------493891244813812906191617
content-length: 1831226
User-Agent: axios/1.7.4
Accept-Encoding: gzip, compress, deflate, br
Host: 127.0.0.1:5032
Connection: keep-alive

----------------------------493891244813812906191617
Content-Disposition: form-data; name="FileBin"

iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAAEAAE......................
----------------------------493891244813812906191617--
#5
Test powershell code
 
Code:
Copy      Help
cls
$filePath = "$home\desktop\Test\Doc.docx"

$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
$fileBase64 = [Convert]::ToBase64String($fileBytes)

$formData = @{
    "fileName" = "Doc.docx"
    "fileBase64" = $fileBase64
}

Invoke-RestMethod -Uri "http://127.0.0.1:5032/saveFile" -Method Post -Body $formData -ContentType 'multipart/form-data'


 Function QM_TcpSocket_server
Code:
Copy      Help
if(getopt(nthreads)>1) ret
AddTrayIcon "cut.ico" "test_TcpSocket_server[]Ctrl+click to end."

#compile "__TcpSocket"
TcpSocket x.ServerStart(5032 &sub.OnClientConnected)

#sub OnClientConnected
function TcpSocket&client $clientIp param !*reserved

;Test file(Pic.png) size is 1342KB; file(Doc.docx) size is 525KB

;The response speed is very fast, but The data reception is unstable, sometimes complete and sometimes incomplete.
client.Receive(_s 2048000)

;The response speed is too slow, but The received data is always complete, the response speed is still very slow even when testing locally.
;client.Receive(_s 0)

out F"SERVER: client request:[]{_s}"

str r="File saved"
client.Send(F"HTTP/1.1 200 OK[]Content-Type: text/plain[]Content-Length: {r.len}[][]{r}")
#6
Much work to create a HTTP server using TCP. At first need to learn how HTTP protocol works. Your server code must parse HTTP headers eg Content-Length to know how much data to read. Then call Receive until all data is read.
#7
Thanks for your suggestion.
Is it possible to first retrieve a small amount of data, such as the first 300 bytes, and then extract the value of `Content-Length: XXXX` from it? Finally, retrieve the full data based on the size specified by that value?
I don’t understand the specific implementation details and am unsure how to modify the code in the existing TcpSocket class.
#8
Don't need to modify TcpSocket class. It's a low-level class, because TCP is a low-level protocol. Use it's functions to create higher-level code, for example a HTTP server.

Don't need GET. When client.Receive receives the first data, it probably contains all HTTP headers, including Content-Length (example below). If possibly there is more data to read, it probably returns 1. But it does not know how much data the client will send. It can return 1 even if there is no more data. The caller must somehow determine whether need to read again (and again...). For it can be used Content-Length. This should work in simple cases.
Quote:POST /saveFile HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; lt-LT) WindowsPowerShell/5.1.26100.2161
Content-Type: multipart/form-data
Host: 127.0.0.1:5033
Content-Length: 18749
Expect: 100-continue
Connection: Keep-Alive

Data here. Possibly only part of data the first time.
#9
Thanks for your help.
Is the following implementation code correct?

Code:
Copy      Help
str rTxt rLen
if(client.Receive(rTxt 300))
,findrx(rTxt "Content-Length: (\d+)" 0 0 rLen 1)
,;out rTxt
,;out rLen
,client.Receive(rTxt val(rLen) 0 1)
,out rTxt
#10
Using the above code, sending a POST request from local PowerShell works successfully.
However, on a remote server, using FRP to create port forwarding and executing the same PowerShell POST request from the remote server, the data received locally is still incomplete.
In any case, the code below is always able to receive the complete data, but the response is very slow.  Huh
 
Code:
Copy      Help
client.Receive(_s 0); out _s
#11
This works with the PowerShell code. Not tested with Tasker, FRP etc.

Member function TcpSocket.HttpRead
Code:
Copy      Help
;/test HttpRead
function! str&method str&path str&headers [str&body]

;Reads HTTP message.
;Returns: 1 if OK, 0 if failed.

;method - receives the HTTP method string, like "GET" or "POST".
;path - receives the request target, like "/" or "/file.html".
;headers - receives the request headers. Raw.
;body - if used, receives the request body. For example the POST data. Raw.

;REMARKS
;If body data is sent, the request headers should contain Content-Length. Data must not be chunked or compressed.


str s
int dataStart flags
;g1
int r = Receive(s 8100 0 flags)
if(r=2) goto gserverError ;;timeout
dataStart=find(s "[][]")
if(dataStart<0)
,if(r=0) goto gBadRequest
,flags=1
,goto g1 ;;wait for complete headers
dataStart+4

ARRAY(str) m
if(findrx(s "^(GET|POST|PUT|PATCH|HEAD|DELETE) (.+) HTTP/1\.1[](?s)(.+?[])[]" 0 0 m)<0) goto gBadRequest
method.swap(m[1])
path.swap(m[2])
headers.swap(m[3])

if(r=1 and findrx(headers "(?m)^Content-Length: *(\d+)" 0 1 _s 1)>=0)
,int conLen=val(_s)
,rep
,,int read=conLen-(s.len-dataStart)
,,if(read<1) break
,,r = Receive(s read 0 1)
,,if(r=0) break
,,if(r=2) goto gserverError ;;timeout

if(&body) body.get(s dataStart)
ret 1

;gBadRequest
Send("HTTP/1.1 400 Bad Request")
ret
;gserverError
Send("HTTP/1.1 500 Internal Server Error")
ret

Macro test HttpRead
 
Code:
Copy      Help
if(getopt(nthreads)>1) ret ;;allow single instance
AddTrayIcon "cut.ico" "test_TcpSocket_server[]Ctrl+click to end."

#compile "__TcpSocket"
TcpSocket x.ServerStart(5032 &sub.OnClientConnected)


#sub OnClientConnected
function TcpSocket&client $clientIp hDlg !*reserved

;This function is called in server side, when a client connects.
;This function runs in separate thread for each client connection.

out F"<><z 0xc0ffff>SERVER: client connected:</z> {clientIp}"

str method path headers data
if(!client.HttpRead(method path headers data)) out "HttpRead failed"; ret

out method
out path
out headers
out data
out data.len

client.Send("HTTP/1.1 200 OK[]Connection: close[][]")

err+ out _error.description

HTTP client in PowerShell
Code:
Copy      Help
cls
$filePath = "C:\Test\customers.sql"

$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
$fileBase64 = [Convert]::ToBase64String($fileBytes)

$formData = @{
    "fileName" = "Doc.docx"
    "fileBase64" = $fileBase64
}

Invoke-RestMethod -Uri "http://127.0.0.1:5032/saveFile" -Method Post -Body $formData -ContentType 'multipart/form-data'
#Invoke-RestMethod -Uri "http://127.0.0.1:5032/saveFile" -Method Get
#12
Thanks again.
I am testing POST requests locally in PowerShell and the open-source workflow automation software n8n, but I am still encountering some instability.
#13
I updated the TcpSocket.HttpRead code. Now should work well.
#14
Thank you so much! Solved my big problem. Heart


Forum Jump:


Users browsing this thread: 2 Guest(s)