Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team


Recommended Posts

  • Topic Author
  • Posted (edited)

    v1.2.64
    - utf8 exported
    - Fix for updateQA file handling that had gone rogue...

    Edited by jgab
    • Thanks 1
    Posted (edited)

    Thank you very much. It's working.

    I would like to mention something. Please see the message when updating QA:
    [25.03.2026][12:51:03][INFO   ][PLUA]: Updated property quickAppVariables to '[]' for QuickApp 1425

    Exactly, it clear quickapp variable(s). Let me be allowed to point out that this is rather undesirable behavior. If the application performs data acquisition and stores its state in variables, it starts from zero. It would be better if the update process did not touch variables. If user consider it appropriate, he can delete/clear them manually. But once the process does this, it cannot restore what was lost. My remark concerns variables only - everything else should still be refreshed.

    Edited by Łukasz997
  • Topic Author
  • Posted
    4 minutes ago, Łukasz997 said:

    Thank you very much. It's working.

    I would like to mention something. Please see the message when updating QA:
    [25.03.2026][12:51:03][INFO   ][PLUA]: Updated property quickAppVariables to '[]' for QuickApp 1425

    Exactly, it clear quickapp variable(s). Let me be allowed to point out that this is rather undesirable behavior. If the application performs data acquisition and stores its state in variables, it starts from zero. It would be better if the update process did not touch variables. If user consider it appropriate, he can delete/clear them manually. But once the process does this, it cannot restore what was lost. My remark concerns variables only - everything else should still be refreshed.

    Well, it doesn't "clear" them. It updates the QA on the HC3 with the quickAppVariables defined for your development QA (--%%var:...)
    I could add a flag to ignore quickVars when updating.

    Posted

    I had no variables defined via --%%var. So in the end, the effect was indeed that the variables were cleared - I think this behavior is reproducible and can be checked easily. Yes, such a flag would be highly desirable.

    Posted

    I saw that new version of PLUA was launched (1.2.64). I would like to ask if you put there a flag causes omitting variable update?
    Or - in other words - what should I do to avoid clearing quickapp variables when updating QA?

  • Topic Author
  • Posted
    56 minutes ago, Łukasz997 said:

    I saw that new version of PLUA was launched (1.2.64). I would like to ask if you put there a flag causes omitting variable update?
    Or - in other words - what should I do to avoid clearing quickapp variables when updating QA?

    No, it didn't make it.
    But I just pushed v1.2.65 with a flag.

    Please login or register to see this code.

    The "nq" at the end means "no quickVars" if provided.
     

    • Thanks 1
  • Topic Author
  • Posted

    v1.2.66 logs a message that it skipped the quickVars...

    • Like 1
    • 3 weeks later...
    Posted

    I have a case where the real HC3 and the emulator behave differently.

     

    The difference seems to be related somehow to UTF-8 content inside an SVG embedded as a base64 data URI.

    The HTML passed to updateView() is basically ASCII-only:

     

    <img src='data:image/svg+xml;base64,...'>

     

    On a real HC3, the same payload renders correctly. In the emulator, some SVGs fail to render (no image in proxy UI). When I transliterate the SVG text content to ASCII (simply remove polish letter in the SVG rendered text label), the same tests start working in the emulator, too.

    It is not as simple as “Polish characters always break it”, because some SVGs containing Polish characters do render correctly. So probably the issue depends on byte position, buffer/chunk boundaries, data length...

    My base64 encoder itself is byte-based and does not use Lua’s utf8 library (which I suspected first). It uses #input and string.byte() on the raw Lua string. So the issue is unlikely to be caused by the encoder calling utf8; it is more likely related to how the emulator transports, decodes, stores, or renders the resulting SVG/data URI when the decoded SVG contains UTF-8 text.

    Again: this is only a lead. I only know that the real HC3 renders it, while the emulator does not in some UTF-8-containing cases.

  • Topic Author
  • Posted

    The UTF-8 fixes applied in the past was messy. I made a new attempt to clean it up. Will hopefully fix your recent problem. v 1.2.75.

    Posted

    I've upgraded to 1.2.76.
    Previously the image was not rendered.

    Now this is even more clear, because hard error occurs. On real HC3 it's working.
    But now I can reproduce the error.

     

    My code is a library to create SVG-based tables. In table constructor theres option: wrap = true|false responsible for wrapping too long lines

    When wrap = true, code cuts the cell content by words and shows it as it fits in multiple lines.

     

    Only in such case error occurs, because only this case activates path using utf8.char and utf8.codes.

     

    To split the text there's a helper used:

    Please login or register to see this code.

     

    I put the utf.codes in pcall and made some debug info: 

    Please login or register to see this code.

     

    Last good previous char is P from writing "TOPł", and "ł" is only polish letter in this file at all.

    I think however, that utf8.codes() in emulator can be suspected. Sorry, I can't be more specific, I hope it helps...

  • Topic Author
  • Posted
    11 minutes ago, Łukasz997 said:

    I've upgraded to 1.2.76.
    Previously the image was not rendered.

    Now this is even more clear, because hard error occurs. On real HC3 it's working.
    But now I can reproduce the error.

     

    My code is a library to create SVG-based tables. In table constructor theres option: wrap = true|false responsible for wrapping too long lines

    When wrap = true, code cuts the cell content by words and shows it as it fits in multiple lines.

     

    Only in such case error occurs, because only this case activates path using utf8.char and utf8.codes.

     

    To split the text there's a helper used:

    Please login or register to see this code.

     

    I put the utf.codes in pcall and made some debug info: 

    Last good previous char is P from writing "TOPł", and "ł" is only polish letter in this file at all.

    I think however, that utf8.codes() in emulator can be suspected. Sorry, I can't be more specific, I hope it helps...


    Can you PM me the SVG table building code?

    Posted

    I can, but this is huge code splitted along many functions.

    But I discovered that something is wrong sooner, with input value to utfChars().
    Please allow me to investigate it deeper and I will come back.

    Posted

    I know the guilty and this is not utf8 but gmatch! 😀

    gmatch is used earlier to return words from text (all but not a space). Like this:

    Please login or register to see this code.

    And word shows "TOP?" instead of "TOPą" (not ł - my mistake)
    A test:

    Please login or register to see this code.

    gives:

    Please login or register to see this code.

    UTF 'ą' is C4 85

    So most likely gmatch("%S+") returns a word truncated like this: 54 4F 50 C4

    In other words, the second byte of the character ą, 85, was treated as a separator/whitespace. Then there's the lone C4, which is invalid UTF-8. print() only reveals the problem.

     

    I can correct it very easily with gmatch pattern change to ("[^ \t\r\n]+"), but after all problem persists.
    I’m glad I nailed it right on the head!

    • Like 1
  • Topic Author
  • Posted

    I ran

    Please login or register to see this link.

    and

    Please login or register to see this link.

    on the plua codebase and fixed a ton of potential errors. (Now part of my release flow)
    Released v1.2.78

    Posted

    With 1.2.78 problem still remains and concerns only one Polish letter: “ą” (rest of it: ĄŚĘĆŻŹÓŁŃśęćżźółń is processed ok with gmatch). Of course, I don’t know whether it also affects other UTF characters, such as hearts, arrows, circles and so on.
    It can be reproduced easily.

    Please login or register to see this code.

     

  • Topic Author
  • Posted

    Ok, v1.2.79.
    -------------
    engine.py:60 now calls locale.setlocale(locale.LC_CTYPE, 'C') before creating the Lua runtime.
    Why: Lua's pattern character classes (%s, %a, %d, ...) are implemented in lstrlib.c via libc's isspace() / isalpha() / etc. — these are locale-dependent. On Polish-locale systems (and pl_PL.UTF-8, C.UTF-8, Windows Polish), isspace(0x85) returns true. Since ą = U+0105 = C4 85 in UTF-8, that 0x85 continuation byte gets eaten by %S+, splitting the character. Forcing C locale makes pattern classes pure-ASCII and consistent across all platforms.
    -------------
    Hopefully that fixes it - can't we all pretend to speak English :-) 

    • Thanks 1
    Posted

    Exactly... now it works with the tiny, poor, little letter “ą”. So thank you even more!

    • Like 1
  • Topic Author
  • Posted (edited)

    How PLua Works — a friendly overview

    This is the "big picture" version of 

    Please login or register to see this link.

    .
    It is meant for QuickApp developers who use plua every day but don't
    particularly care about the Python internals — you just want a mental
    model of what's happening when you press Run.

    If you ever need the deep-dive (every module, every queue, every class),
    the full 

    Please login or register to see this link.

     is one click away.


    What plua actually is

    Plua is two things bolted together:

    1. A Lua interpreter that runs on your laptop. Fibaro's Home Center 3
      (HC3) is itself a Lua machine, and plua runs the same Lua 5.4 dialect.
    2. A fake HC3 written in Lua. A few thousand lines of Lua code
      pretend to be a Home Center 3 — there is a device list, a fibaro.*
      API, a UI engine, scenes, global variables, the works. Your QuickApp
      talks to this fake HC3 exactly the way it would talk to a real one.

    The Lua interpreter is hosted by Python (using a library called

    Please login or register to see this link.

    ), because Python gives us easy
    access to networking, an HTTP server, and a window manager — all things
    that a plain Lua interpreter doesn't have built-in.

    So, very roughly:

    Please login or register to see this code.


    The four layers, top to bottom

    1. Your QuickApp

    Plain Lua. The same code you would upload to a real HC3. It calls
    fibaro.call(...), self:updateProperty(...), setTimeout(...) and so
    on, blissfully unaware that it's running on your laptop.

    2. The fake HC3 (src/lua/fibaro/)

    Also pure Lua. A singleton object called Emu is the heart of it:

    • It keeps a table of every "device" that exists in this fake HC3 (your
      QA, plus any child devices, plus a couple of housekeeping entries).
    • It implements the fibaro.* functions and the /api/... REST routes.
    • It compiles your --%%u: UI headers into something the browser can
      draw.
    • When you call fibaro.call(123, "turnOn"), Emu decides whether to
      handle it locally, forward it to a real HC3, or look it up in an
      in-memory store — that decision is what we call modes (see below).

    The user-facing class QuickApp lives here too. When plua starts your
    script, it parses the --%% headers, builds a synthetic device record,
    registers it with Emu, and calls your onInit().

    3. The Python core (src/plua/)

    A small number of Python modules that exist mainly to give the Lua side
    super-powers it doesn't have on its own:

    • engine.py — owns the Lua VM and the asyncio event loop. Think of
      this as "the heartbeat".
    • timers.py — when you call setTimeout(fn, 1000) in Lua, this is
      what schedules it.
    • lua_bindings.py — the bridge. Anything Python wants to expose to
      Lua is decorated with @export_to_lua and shows up in Lua as
      _PY.somefunction(). (You almost never call _PY directly — Lua
      wrappers like net.HTTPClient do that for you.)
    • fastapi_process.py — runs the small web server that powers the UI
      windows and the REST API.
    • window_manager.py — opens browser windows for QA UIs and
      remembers their position.

    4. The network library (src/pylib/)

    Roughly one Python file per protocol: HTTP, TCP, UDP, WebSocket, MQTT,
    plus filesystem access. These are the things net.HTTPClient,
    net.TCPSocket, etc. quietly call into. They don't know anything about
    Fibaro — they just do networking and report results back to Lua via a
    callback.


    What happens when you press F5

    A simplified play-by-play of >plua --fibaro myqa.lua:

    1. Python starts up. It reads your command-line flags, makes sure
      the API port is free, and starts the small FastAPI web server in the
      background.
    2. The Lua VM is created and a few hundred Python helpers are
      exposed to it under the global _PY table.
    3. init.lua runs. It patches some things (UTF-8, timers,
      print) and waits to be told what file to load.
    4. fibaro.lua runs (because of --fibaro). This brings the fake
      HC3 to life: it creates Emu, sets up the API router, and tells
      init.lua "actually, let me load the user's file."
    5. Your file is loaded. plua reads the --%% headers, builds a
      device record, and creates an instance of your QuickApp class.
      QuickApp:onInit() is called.
    6. The event loop runs. Timers fire, HTTP responses arrive, UI
      clicks come in from the browser, the QA reacts. This continues until
      --run-for says it's time to quit (default: at least 1 second, then
      exit when nothing is pending).

    Talking to the outside world

    When your QA does net.HTTPClient():request(url, ...):

    Please login or register to see this code.

    The same pattern works for TCP, UDP, WebSocket, MQTT, timers, and even
    "a HTTP request hit our FastAPI server, please handle it in Lua". It is
    always: register a callback ID in Lua → do work in Python → drop the
    result on a queue → Lua gets called back. This is what lets a single Lua
    thread coexist with everything async without ever blocking.


    The three modes

    A QA always runs in one of three modes (set by --%%offline:true,
    --%%proxy:true, or neither):

    Mode What fibaro.call(...) actually hits
    Online (default) The fake HC3 first, and anything it can't handle is forwarded to your real HC3 over HTTP. You need credentials in .env.
    Offline Only the fake HC3, plus an in-memory store seeded with some realistic dummy devices. No network at all. Great for CI and trains.
    Proxy Your QA runs locally and a tiny stub QA is installed on the real HC3. UI clicks and HC3 events tunnel back to plua over a WebSocket, so you can iterate locally while still reacting to the real device.

    The UI windows

    When a QA has --%%desktop:true, plua opens a browser window pointed at
    its built-in web server. The page (quickapp_ui.html) draws your
    buttons, sliders, and labels by fetching /plua/quickApp/<id>/info.

    When you click a button:

    1. The browser POSTs to /api/plugins/callUIEvent.
    2. FastAPI hands the request to the engine.
    3. The engine looks up the QA, calls the right handler method.
    4. If the handler calls self:updateView(...), the engine pushes the
      change back to the browser over a WebSocket so the UI updates live.

    You don't have to think about any of this — just write onReleased
    handlers and call updateView.


    Where things live (cheat sheet)

    Please login or register to see this code.

    Things you'll touch as a QA developer: your own .lua file, the
    HC3 credentials in .env, and sometimes a header in .project.

    Things you'll never need to touch (but now know exist): Python core,
    the FastAPI server, the queue plumbing.


    Where to go next

    • Want a concrete starter QA? plua --init-qa scaffolds a project.
    • Want to see every API your QA can call? Ask Copilot for
      /quickapp-api.
    • Want common patterns (timers, polling, child devices)? Ask for
      /quickapp-patterns.
    • Want the deep architecture? Read 

      Please login or register to see this link.

      .
    • Stuck? 

      Please login or register to see this link.


      and 

      Please login or register to see this link.


      cover the common pitfalls.
    Edited by jgab
    Posted

    So Python acts as the "host" of the whole operation? It does not implement the full Lua execution itself - tokenizer/lexer, parser, bytecode compiler, VM, call stack, garbage collector, etc. Instead, Python creates the runtime environment and embeds the native Lua engine, which then executes the Lua code?

    That engine inherits some host-level environment settings, such as locale. So the bug like above was not really in gmatch itself, but lower in the runtime environment, and Lua merely inherited the resulting behavior.
    Am I understand it correctly?

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest
    Reply to this topic...

    ×   Pasted as rich text.   Paste as plain text instead

      Only 75 emoji are allowed.

    ×   Your link has been automatically embedded.   Display as a link instead

    ×   Your previous content has been restored.   Clear editor

    ×   You cannot paste images directly. Upload or insert images from URL.

    ×
    ×
    • Create New...