Jump to content

lynkfs

Moderators
  • Content Count

    640
  • Joined

  • Last visited

  • Days Won

    102

Everything posted by lynkfs

  1. This link is from feb 2019 (nasa related), so may work : https://www.jakenherman.com/articles/2019-02/push-notifi-cordova-firebase (there are more links as a result of googling 'cordova push notifications plugin 2019', which may be of relevance)
  2. lynkfs

    Waiting for controls to be ready

    could the 'readyexecute' method be repurposed so that code changes to existing projects could be avoided ?
  3. lynkfs

    bluetooth

    How to connect webapp to a bluetooth enabled printer. Basically there are 2 avenues to do that : a) going native and b) the web bluetooth api Going native This involves using PhoneGap or Cordova, plus installing a plugin to make the bluetooth capacities of the mobile target platform available in the js world. I found this plugin on github, a cordova plugin, so used cordova rather than phonegap. The install sequence for a clean install of everything looks something like see https://cordova.apache.org/docs/en/latest/guide/cli/ install node install npm install cordova : npm install -g cordova install pre-requisites : see https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html#requirements-and-support java development kit (jdk) gradle android studio / android sdk set environment and path variables JAVA_HOME ANDROID_HOME PATH create app structure create app : cordova create bluetoothprinter com.smartmobilestudio.bluetoothprinter BluetoothPrinter install plugin cordova plugin add https://github.com/srehanuddin/Cordova-Plugin-Bluetooth-Printer.git create remainder of app add platform : cordova platform add android create SMS project copy SMS project output into the cordova app structure (www directory) build app : cordova build android install apk file on android Amazingly this all works out. Some gotcha's : - environment and path variables don't allow spaces, so for instance .../Program Files/... has to be shortened to .../Progra~1/... - cordova now requires plugins to have their own package.json file. The selected plugin doesn't have that, so I had to download the git repository, create a package.json file and install the plugin from local (cordova plugin add cordova-plugin-bluetooth-printer --save --searchpath ../plugin) - the SMS project needs these entries in its template index file <script type="text/javascript" charset="utf-8" src="cordova.js"></script> <script type="text/javascript" charset="utf-8" src="BluetoothPrinter.js"></script> and compile may end up in errors. However the cordova build process takes care of that and produces a viable apk file. It is a bit of a process to go through, but works out well in the end.
  4. lynkfs

    bluetooth

    For this app I used the HOP-H58 bluetooth printer. It comes with bluetooth and usb connectivity, and even a socket for a cash drawer. At an unbeatable price of $26 USD delivered (!), this gives me a mobile POS (invoice print) solution, where only the printer and a mobile phone is needed. Amazing.
  5. lynkfs

    kodi grid

    @lennart posted a reference to a scrollable grid in Delphi Developer. See post here. Since he produced this grid in Smart Pascal, it might be of interest to forum members. This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns. The code he posted doesn't work in my (latest) version of Smart, but with a few tweaks here and there I got it going. At least for strings that is. Icons/images are not fully implemented in the source he posted, but I'll take another look. Won't be too difficult. I like the marching ants selection. See demo. kodi := TWbIconView.Create(self); kodi.setbounds(10,10,500,500); //by way of demo : just split any sentence into words and use these as variable length cell items var s : string := #'This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns.'; var a : array of string := StrSplit(s,' '); for var i := 0 to a.length-1 do begin kodi.AddText(a[i]); end;
  6. lynkfs

    Combobox items work like "read-only" mode

    don't use the items object : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components W3ComboBox1.add('Line 1'); W3ComboBox1.add('Line 2'); W3ComboBox1.add('Line 3'); end; procedure TForm1.W3Button1Click(Sender: TObject); begin W3ComboBox1.Clear; end;
  7. lynkfs

    tensorflow

    edited Been a while since I had a look at Googles Tensorflow deep learning library (js version) In the meantime this library has expanded quite a bit, most notably it enables using pre-built models for a variety of tasks. This one takes any image and detects up to 90 categories of objects in the image (persons, dogs, traffic lights etc) The library correctly identifies 2 objects of type "person" in the above image (image 620*408 pixels, bbox coordinates x,y,width,height) Usage is incredibly simple : load the libraries, display an image in the browser and issue a detect command var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs'; console.log('init tensorflow'); document.head.appendChild(Script); Script.onload := procedure begin console.log('tensorflow loaded'); var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd'; console.log('init coco-ssd'); document.head.appendChild(Script); Script.onload := procedure begin console.log('coco-ssd loaded'); var Image1 := TW3Image.Create(Self); Image1.SetBounds(0, 0, 620, 408); Image1.src := 'puydesancy.jpg'; var img := Image1.handle; asm //quick and dirty : cocoSsd.load().then(model => { // detect objects in the image. model.detect(@img).then(predictions => { console.log('Predictions: ', predictions); }); }); end; end; end; (should have checked for image1.onload). Anyway, demo here. Works from server only due to cors constraints, otherwise results in 'tainted image' errors. edited and another random image
  8. lynkfs

    tensorflow

    it is a bit slow timing for this example : start: 0.0849609375ms init tensorflow : 0.936767578125ms tensorflow loaded : 1017.56591796875ms init coco-ssd : 1017.623779296875ms coco-ssd loaded : 1421.75390625ms get image : 1422.751708984375ms Predictions: 45869.56982421875ms Firefox has the same sort of response times, Edge doesn't support console timers. It is basically the time to load the mobilenet model itself from the google servers, which is responsible for the large timegap between get image and the resulting predictions. GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/model.json" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard5of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard4of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard3of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard2of5" GET "https://storage.googleapis.com/tfjs-models/savedmodel/ssdlite_mobilenet_v2/group1-shard1of5" It should be possible to cache all of these files, or store them on a SBC
  9. lynkfs

    console

    I recently revisited the console object, available in all browsers, and discovered some newbies I didn't know about. The methods and properties below may be sometimes useful for debugging purposes. The console object is tied to the global window object, so access either by browserapi.window.console.log('whatever'); or define a reference to the window object : var window external 'window': variant; and call like window.console.dirxml(self); Some console methods are wrapped in SmartCL.System (like writeln ea) and SmartCL.Console The console.dirxml() above gives an xml representation of any object, which is way better than the 'object Object' result of a console.log(some object) instruction For performance testing, console also offers native timers console.time() console.timeLog() console.timeEnd() The above respectively initiate, report and destroy a named timer. The maximum nr of concurrent timers is 10,000 (!) To see how this works I created a stock new visual project with a single form and no visual controls. A timer was inserted at the beginning of the generated js projectfile, at the initialization of the form and at the ApplicationStarting, InitializeForm, InitializeObject and Resize events. Results : console.timeLog top of main.js : 0.0009765625 ms Initialization 10.574951171875 ms applicationstarting 17.234130859375 ms InitializeObject 21.23095703125 ms ReSize 62.385986328125 ms ReSize 101.25 ms InitializeForm 225.994140625 ms ReSize 226.757080078125 ms Wondering about the multiple ReSize calls and the relatively large time lag between InitializeObject and InitializeForm. There is another feature built into browsers re performance : the window.performance object with its own api's The most useful methods of this object are performance.now(), which gives a high res timestamp, and performance.toJSON() which gives a json representation of the attributes of the performance object Below the 'console.timer' and 'performance.now' results of a minimum project with 1 form and no visual controls in the native framework : console.timeLog performance.now() top of main.js : 0.001953125 ms / 118.28000005334616 timestamps Initialization 2.16186523437 ms / 120.43999996967614 InitializeForm 3.61083984375 ms / 121.92000006325543 InitializeObject 4.32080078125 ms / 122.65000003390014 and some values for the attributes of the performance object itself (console.log(window.JSON.stringify(window.performance.toJSON()));) timeOrigin : 1564383720413.3972 timestamps navigationStart: 1564383720413 redirectStart: 0 redirectEnd: 0 fetchStart: 1564383720414 domainLookupStart: 1564383720418 domainLookupEnd: 1564383720418 connectStart: 1564383720418 connectEnd: 1564383720418 secureConnectionStart: 0 requestStart: 1564383720418 responseStart: 1564383720423 responseEnd: 1564383720426 unloadEventStart: 1564383720432 unloadEventEnd: 1564383720432 domLoading: 1564383720435 domInteractive: 0 domContentLoadedEventStart:0 domContentLoadedEventEnd: 0 domComplete: 0 loadEventStart: 0 loadEventEnd: 0
  10. lynkfs

    Waiting for controls to be ready

    Some interesting decisions to be made. side note : the native/shoestring rtl uses MutationObservers exclusively, which worked well for me. Interested to see what the speed difference is
  11. lynkfs

    Responsive Design

    Responsive design in essence consists of 2 measures : style all components as best as possible depending on screen size (larger buttons on mobile, proportional font sizing etc) tweak the layout of forms so that it works best on any given screen size 1) Screen size dependent styling can be done in code and/or is done as other frameworks do by including media queries in the stylesheet. Media queries basically activate different parts of a stylesheet depending on the actual screen size. Smart doesn't implement this in its shipped stylesheets, but there is no reason it can't be done. Just a bit of work. 2) Tweaking the form layout in Smart is possible by doing it in code depending on screensize and orientation - a bit of a pain really using the Layout Manager using some css grid framework or ui kit. For website development I've used the Frow framework quite a bit, but there are many others. using anchors and the CSS grid (which is basically the topic of this post) The new anchors are fantastic, but don't really help in responsive design. Browser window resizing doesn't proportionally resize anchored components. That is unless they are dimensioned depending on screen size beforehand : var W3Panel0 : TW3Panel := TW3Panel.Create(self); W3Panel0.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel0.Top := trunc(browserapi.window.innerHeight*0.05); W3Panel0.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel0.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel0.Anchors := [akLeft, akRight, akTop]; // var W3Panel1 : TW3Panel := TW3Panel.Create(self); W3Panel1.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel1.Top := trunc(browserapi.window.innerHeight*0.15); W3Panel1.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel1.Height := trunc(browserapi.window.innerHeight*0.70); W3Panel1.Anchors := [akLeft, akRight, akTop, akBottom]; W3Panel1.NativeScrolling := true; // var W3Panel2 : TW3Panel := TW3Panel.Create(self); W3Panel2.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel2.Top := trunc(browserapi.window.innerHeight*0.85); W3Panel2.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel2.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel2.Anchors := [akLeft, akRight, akBottom]; As an example the code above produces a header (always on top), a footer (always on the bottom) and a scrollable body inbetween with some proportional margins. The good thing is it works well on all screen sizes, and even on orientation changes, without having to code anything else. Instead of using a grid system with media queries, or doing it in code, it is also possible to use the built-in css grid to get a responsive design. Much easier, see article. If I just plonk the css and html of the first example in that article into the structure above, this is how it ends up looking : Demo Resize browser window to see this in effect (or see it in the responsinator) Not the end-all of all possible responsive design options, but it may fit the bill sometimes. Note : the built-in chrome browser doesn't understand the css grid, so execute from file or server.
  12. lynkfs

    Waiting for controls to be ready

    To make this work using MutationObservers I would probably do something like this: demo, project Essentially the TestPanel is put here under the surveillance of a MutationObserver, which triggers when all changes to the panel have been applied (including the original sizing). After that it is ready to accept the label etc. procedure TTestPanel.InitializeObject; begin inherited; WriteLn('InitializeObject called -> Creating Label'); self.observe; self.handle.addEventListener('readytogo', procedure(e:variant) begin writeln('heard readytogo'); var Lab:=TW3Label.Create(Self); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); WriteLn('InitializeObject done'); end); end; The code in this demo works, but is a bit ugly. It would be better to include the observe method standard in a constructor, put it in the TControlHandle rather than the TPanel, rewrite the eventlistener similar to the standard ReadyExecute method etc. But hey, demo. As a matter of fact, using the standard ReadyExecute method instead of MutationObservers works here as well. I think the latter is a bit better though as observers don't rely on timeouts and a single observer does handle multiple changes. /* self.Handle.ReadyExecute(procedure() begin var Lab:=TW3Label.Create(Self); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); WriteLn('InitializeObject done'); end); */ Maybe the rtl could be made to handle all child inserts in this manner
  13. lynkfs

    file system

    The original html5 spec had a file api which allowed to read and write files clientside (the File Directories and System Api). This was only fully implemented by Chrome at one point, but even then Chrome needed to be started with a switch (-allow-file-access-from-files). Today clientside storage is limited to cookies, local/session storage, indexedDB and the Cache API. While I understand the reasons for limiting file handling on the client, it is sometimes annoying. Fortunately some pretty creative solutions have emerged. One of them is Filer. This is a nodejs fs filesystem look-a-like which uses indexedDB for storage, and runs in the browser. Basically it allows for a complete filesystem clientside. Not bad at all. To continue stacking : Filer is stacked on top of indexedDB. Stack nohost on top of Filer and you have a web fileserver (based on service workers) in the browser. Amazing. Will certainly try this out.
  14. lynkfs

    file system

    Scripts need to be loaded once only. I usually do that in the Form.InitializeForm section, but you can do it anywhere (as long as the script is loaded before use) procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Script := browserapi.document.createElement('script'); Script.src := 'res/filer.js'; browserapi.document.head.appendChild(Script); Script.onload := procedure begin writeln('script loaded'); ... the asynchronous onload event triggers after the script has loaded Alternatively you can use the $R directive
  15. lynkfs

    file system

    last one. It becomes even better when stacking 'nohost' on top of it all. This demo shows that it is possible to fire up a clientside file server in the browser ! (you might have to refresh once, check console ctr-shift-i)
  16. lynkfs

    file system

    And here is a quick File Explorer like project for a client side file system Note : uploaded the first 2 files of the smart directory "Projects/Contest Demos/Afternoon Walk" Just click on treeview until you get there
  17. lynkfs

    file system

    A quick test on persistence : indexedDB (and hence the client file system) is persistent per device. Which means that once a filesystem is created, it will be there the next time the app is activated. Some caveats though it is pretty safe, but not 100% so. Browsers may delete a database if there is not enough diskspace. Why and when differs between browsers somewhat too. storage access is regulated by 'same origin'. In the demo above the file system is accessible in the lynkfs.com domain, but not in ibm.com Which is ok. firing up multiple instances of chrome does not disrupt access, the db is accessible in all instances there is no cross-access between browsers (firefox doesn't see indexDB instances created in Chrome) users may clear browser cache/memory/storage All in all not too bad
  18. lynkfs

    Learning Smart Mobile Studio

    Look at Primož Gabrijelčič's book on SMS, available on https://leanpub.com/asmartbook Very valuable and I still consult it on a regular basis There is some info on http://www.pp4s.co.uk/main/smart-gettingstarted.html as well
  19. lynkfs

    file system

    This code works (File system in the browser) create and mount a FileSystem create a directory write a file with some contents read the stats of this file read the contents of this file write another file list the directory contents in a memo Demo procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Script := browserapi.document.createElement('script'); Script.src := 'res/filer.js'; //https://raw.githubusercontent.com/filerjs/filer/develop/dist/filer.js browserapi.document.head.appendChild(Script); Script.onload := procedure begin writeln('filer loaded'); var Filer := browserapi.window.Filer; writeln('instance created'); var fs : variant := new JObject; asm @fs = new (@Filer).FileSystem({ name: "myfilesystem01", flags: [ 'FORMAT' ] }); end; writeln('filesystem created'); //mkdir fs.mkdir('/docs', procedure(err:boolean) begin if (err) then writeln('Unable to create /docs dir'); end); //write file fs.writeFile('/docs/firstDoc.txt', 'Hello World', procedure(err:boolean) begin if (err) then writeln('Unable to write /docs/firstDoc.txt'); end); //stats fs.stat('/docs/firstDoc.txt', procedure(err, stats:variant) begin if (err) then writeln ('Unable to stat /docs/first.txt') else writeln(stats); end); // Read file fs.readFile('/docs/firstDoc.txt', 'utf8', procedure (err:boolean; data:string ) begin if (err) then writeln ('Unable to read /docs/firstDoc.txt') else writeln(data); end); //write second file fs.writeFile('/docs/secondDoc.txt', 'Brave new world', procedure(err:boolean) begin if (err) then writeln('Unable to write /docs/secondDoc.txt'); end); //get reference to shell var sh : variant := new JObject; asm @sh = new (@fs).Shell(); end; // list directory, shallow listing sh.ls('/docs', procedure(err:boolean; entries: array of variant) begin if (err) then writeln ('Unable to read /docs directory') else begin W3Memo1.Text := '/docs' + #10; for var i := 0 to entries.length -1 do begin writeln(entries[i].name); W3Memo1.Text := W3Memo1.Text + ' ' + entries[i].name + #10; end; end; end); end; end; I like this a lot note : works from file or server, not in the ide
  20. lynkfs

    Favicon

    The standard generated index file in smart doesn't contain a link tag for a favicon (something like <link id="favicon" rel="icon" href="res/favicon.png" type="image/png" sizes="16x16"> However this can be added in code (or add it manually to the default.html file in the template directory, or add it to a Custom Template in the ide) In code : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var link := w3_createHtmlElement('link'); link.id := 'favicon'; link.href := 'res/favicon.png'; link.rel := 'icon'; link.&type := 'image/png'; link.sizes := '16x16'; browserapi.document.getElementsByTagName('head')[0].appendChild(link); end; If you want to change the icon later on, you can do that too procedure TForm1.W3Button1Click(Sender: TObject); begin var favicon := browserapi.document.getElementById('favicon'); favicon.href := 'res/favicon2.png'; end;
  21. lynkfs

    Responsive Design

    If this shortcut approach (providing html code directly in smart code) works for you, then adding event handlers has to be done in html too. Just add an ID to the img element so you can find the image element in the dom, and add an eventlistener to it : <!-- Photo by Drew Farwell on Unsplash --> <img id="mypic01" src="https://images.unsplash.com/photo-1513309914637-65c20a5962e1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3450&q=80" alt="Several friends doing a toast"> <figcaption><h3>Drake Equation</h3></figcaption> etc... browserapi.document.getElementById("mypic01").addEventListener("click", procedure(event:variant) begin writeln('clicked'); end); Note that once you start mixing direct dom manipulation with standard rtl processing, you're mixing different worlds. Things might look the same but not necessarily have the same effect. The onclick event handler above is not the same as the standard smart OnClick handling etc. See what works for you.
  22. lynkfs

    printing

    I need to print receipts from a webpage (intranet). The difficulty is not so much to get a print happening on some printer, but to do it in such a way that the usual printer selection dialog gets bypassed. So basically a form with a print button which, when pressed, prints a receipt to a designated printer straightaway, without every time asking the user to select a printer. The normal print methods (window.print(), google cloud print, external print.js libraries) do not solve that problem. The only reference I found that might work is the ePOS_SDK from Epson, which can be used with some of their receipt printers. See https://www.epson.com.au/pos/products/software/index.asp which lists sdk versions for iOs/Android, but also for webapps / javascript. If someone has solved a similar problem then feedback is appreciated.
  23. lynkfs

    printing

    Edited. The final piece of the puzzle is to find out if it is possible to use IPP printing directly from the browser, bypassing the need for a node server. The answer, surprisingly, is that it is possible. There are some caveats, but essentially it can be done. First thing is to get the node.js project and transform it into something a browser understands. The way to do this is to use Browserify, which will bundle the project file, the ipp library and all of its dependencies : install browserify (npm install browserify) Save the first node.js code example of the previous post (the code which lists printer capabilities) to file, say 'ipp.js' Execute 'browserify ipp.js -o bundle.js'. This produces a 'bundle.js' file which incorporates everything the browser needs. The minimum html file with a reference to this file included : <!doctype html> <html> <head> <title>Browserify</title> </head> <body> <script src="bundle.js"></script> </body> </html> Executing this should produce the list of printer capabilities. However it doesn't and a CORS error occurs. In short : essentially scripts calling other scripts (which happens here) is forbidden if these scripts are not on the same origin (domain). CORS is a method to relax that, but it requires the non-origin server to send a specific header in its response (Access-Control-Allow-Origin). If not, browsers will throw an error. Apparently the ipp library doesn't handle this CORS requirement. The only way to circumvent this is to either use a proxy server (see article link) or to instruct the browser to disregard same origin checking alltogether. The latter option involves firing up a separate Chrome instance with some switches : start chrome --args --disable-web-security --user-data-dir="C:\chrome_temp" file:///C:/Users/...../print/test.html Works for me. Executing the html file above generates the expected output. Edited : If the app is produced using Phonegap/Cordova apparently it is possible to handle CORS by whitelisting the various origin domainnames. Have not tried that. Is IPP worth the trouble ? I think so if the use-case is what started this post : having to print from a webpage (intranet) without having to go through a printer selection dialog every time a print is initiated. In my case dealing with a retail ePos application where every transaction needs a printed docket. Afaik the only other option is to use specialised printers like the ones in the epson family.
  24. lynkfs

    printing

    Edited. And then there is IPP : Internet Printing Protocol This protocol allows direct access to networked printers, ask for their capabilities, prepare print jobs, manage print queues, execute print jobs and get results. Exactly what I was after. I read through the documentation on the website of "The Printer Working Group", which looks after the standardisation of this protocol. (http://www.pwg.org/ipp/ippguide.html). To actually use this protocol, there are various IPP client libraries available (C, Java, Python). Fortunately there is a node.js variant as well (https://github.com/williamkapke/ipp). Both these links give some examples on how to use IPP in real world situations However the code examples on both sites contain many errors. Amazing. On the plus side, apparently some 98% of all networkable printers have an IPP driver installed by default, and should be adressable this way. To test this all out, I selected a printer on my network. The first thing to find out is the identification of the printer. IPP has its own protocol : ipp(s)://<printer uri> where the printer uri is the printers ip address followed by a path. Example : ipp://192.168.1.233/ipp/print Behind the scenes all traffic is actually routed over http(s), and printer uri's can also be specified as http over port 631. Example : http://192.168.1.233:631/ipp/print The ip address can usually be found in the OS printer settings somewhere and the path is mostly standardised to be just '/ipp/print' Not too bad. After installing the node.js ipp library (npm install ipp) the following node.js code queries the printers capabilities var ipp = require("ipp"); var printer = ipp.Printer("http://192.168.1.233:631/ipp/print"); var msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "image/pwg-raster", "requested-attributes": ["printer-description", "job-template", "media-col-database"] } }; printer.execute("Get-Printer-Attributes", msg, function(err, res) { console.log(err); console.log(res); }); This produces quite a list. To print a textfile to this printer, I got this code working var ipp = require("ipp"); var printer = ipp.Printer("http://192.168.1.233:631/ipp/print"); var fs = require("fs"); fs.readFile("example.txt", function(err, content) { if (err) throw err; var msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "application/octet-stream" }, data: content }; printer.execute("Print-Job", msg, function(err, res) { console.log(err); console.log(res); }); }); Edited : The javascript sources above can be produced by a regular smart project : create a new node project and replace unit1 with unit Unit1; interface uses nodeBasics; //https://forums.smartmobilestudio.com/topic/4652-node-ground-zero/?tab=comments#comment-23115 type TNodeProgram = class(TObject) public constructor Create; virtual; ipp, msg, printer : variant; procedure callback(err, res: variant); procedure Execute; end; implementation constructor TNodeProgram.Create; begin inherited Create; ipp := RequireModule('ipp'); printer := ipp.Printer("http://192.168.1.233:631/ipp/print"); msg := new JObject; asm @msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "image/pwg-raster", "requested-attributes": ["printer-description", "job-template", "media-col-database"] } }; end; end; procedure TNodeProgram.callback(err, res: variant); begin console.log(err); console.log(res); end; procedure TNodeProgram.Execute; begin printer.execute("Get-Printer-Attributes", msg, @callback); end; end. note : unfortunately we can't make up anonymous classes with hyphens in the classname, otherwise the msg variable could have been constructed as var msg = class operation-attributes-tag = class requesting-user-name = "John Doe", document-format = "image/pwg-raster", requested-attributes : array[0..2] of string = ["printer-description", "job-template", "media-col-database"]; end; end; Next step is to see if this can be made to work in the browser as well
  25. lynkfs

    printing

    Printers used to be dumb peripherals. Not so with the latest receipt printers (epson). They have actually an apache webserver built in, and the printer can act as the hub for displays, scanners and other (dumb) peripherals. amazing. Connecting them to Smart looks like not too difficult.
×