[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: OpenScan\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": "2024-10 ReCap.md",
    "content": "# OpenScan - Bringing 3D scanning to the masses - what was, what is and what can be?\n\n***In the following sections, the OpenScan project is outlined. It is important to understand the background and the bumpy ride that brought us here in order to lay a foundation for future developments***\n\n___\n___\n\n## Background\nBack in 2017, I was looking for an accessible 3D scanning solution and quickly realised, that there was none. All existing solutions either came with a hefty price tag or were not able to create decent 3D scans. \n\nI stumbled upon photogrammetry as a very powerful tool and played around with it as it only requires a camera. Refining the lighting and capture settings, I was able to get very decent scans. When I was able to copy both my mailbox as well as my \"security\" home key, I realized that photogrammetry can yield great results. \n\nBeing a lazy person, I started to automate the process using an *Arduino Nano*, which was tilting and rotating the object. Adding a ringlight to the camera for better lighting and most of the capture process was automated. It is important to state, that I had NO background in electronics, programming or community management. Every of those skills (and many others) needed to evolve during the development process.\n\n**Nothing would have been possible without the immense support and contribution from the very open-minded community. Having so many useful resources publicly available has always been a major cornerstone of the project. For this and many other reasons, being open-source is a core value of the project. Becoming able to contribute to the open-source movement is such an amazing honor!**\n\nIn 2019, I changed the plattform in favor of the *Raspberry Pi*. This step came with new challenges but also a lot of new options to increase the scanners capabilities. My main goal was to **automate and simplify as much of the photogrammetry capture process as much as possible - maybe even create a one-click-3d-scanning-solution.**\n\nIn 2021, the optional *OpenScan Cloud* was launched. This lowered the entry barrier even further, as many users either did not want or could not do the processing locally (as this always requires some software knowledge as well as a capable computer). Thanks to the support through Patreon & BuyMeACoffee, the OpenScan Cloud maintains its state till today.\n\nEarly 2022 Arducam released their *16mp IMX519* camera with focus control. This allowed an even finer control of the scanning process. Adding *focus stacking* to the process increased the quality of the resulting 3D models even further. To this point, the OpenScan project grew on various channels and reached people in over 70 countries. \n\nUnfortunately, the evolution of the project created an almost unmanageable pile of documents/codes/files across various platforms and github repositories. There have been several attempts of restructuring and reorganizing the whole structure, but by this point, the sheer amount of data has been totally overwhelming for me. Producing and distributing scanner kits as a business brought its own amount of challenges (customer support, accounting, production, delivery bottlenecks, legal issues ...).\nBy this time, i felt almost incapable of further developing the project and almost abandonned it completely.\n\n**Fortunately, there have been several idealistic and very eager community members (especially on the OpenScan Discord), which not only welcomed new OpenScan users and patiently helped with all the existing and known issues of the system. But they also started several community developments, which solved many of the existing hardware and software issues.**\n\n___\n___\n\n## Current State of the project\n\n| great | not so great |\n| -- | -- |\n|- modularity <br>- wide user base across all continents <br>- used for archeology, dental, research, creation of gaming assets, reverse engineering, miniatures <br>- the only open-source and low-cost 3D scanning solution <br>- high quality 3D models <br>- a lot of potential <br>- great understanding of the underlying principles and available knowledge |- disconnect between official and community <br>- many (known) issues in the offical versions --> bad user experience <br>- relatively high effort needed to get started <br>- scattered and outdated documentation <br>- no organizational structure <br>- no structure for contribution and appreciation thereof  |\n\n___\n### Hardware\n\n| | OpenScan Mini V1 | OpenScan Classic | OpenScan Mini V2 | OpenScan Midi | \n| -- | -- | -- | -- |  -- | \n| **state** | official | official | community | community |\n| **more details** | [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanMini/) | [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanClassic/)| [github](https://github.com/OpenScan-org/OpenScan-Design/tree/main?tab=readme-ov-file#openscan-mini-v2) |  [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanClassic/) |\n\n___\n### Firmware/Software\n\n| | Official Firmware | \"Patreon Beta\" | OpenScan Meanwhile | OpenScan Composer  \n| -- | -- | -- |  -- | -- | \n| **state** | official | partly official | community | community |\n| **more details** | [github](https://openscan-org.github.io/OpenScan-Doc/firmware/setup/) | [patreon (free)](https://www.patreon.com/posts/beta-firmware-2-86937106) | [github](https://github.com/stealthizer/OpenScan2/tree/2024-1o)<br>[roadmap](https://miro.com/app/board/uXjVNrJGlbQ=/) | [OpenScanComposer.com](https://www.openscancomposer.com/)|\n\n___\n### Community\n\n| Channel | Focus |\n| -- | --|\n| [Discord](https://discord.gg/gpaKWPpWtG) | - community support <br>- coordination of development |\n| [OpenScan.eu](http://openscan.eu) | - official website <br>- entry point for most new users |\n| [reddit/r/OpenScan](https://www.reddit.com/r/OpenScan/) | - community support<br>- show and tell|\n\n___\n### Electronics\n\nThere are two core components: \n* The **pi shield** directly connects on top of the Raspberry Pi and is used to interfer with the motors, camera and lighting.\n* The **ringlight module** for standard pi camera form factor cameras allows for optimal illumination. \n\nIt is noteworthy, that these two PCBs are not strictly necessary to build a 3D scanner, but greatly simplifies the overall process.\n\n___\n#### Pi Shield\n\n| | Green Shield (pre-soldered) | Green Shield (solder yourself) | Black Shield | 4 Axis Shield | \n| -- | -- | -- | -- |  -- | \n| **state** | official | official | community |  unpublished | \n| **more details** | link | link | link | link |\n\n___\n#### Ringlight\n\n___\n### OpenScanCloud\n\nThe [OpenScanCloud](https://github.com/OpenScan-org/OpenScanCloud) is a free and donation-based online photogrammetry processing pipeline with increasing popularity. Its simplicity (one-click) allows users to avoid the need for local processing power and knowledge of a dedicated software.\n**It is solely financed through Patreon donations and there will never be any kind of monetization crippling its functionality!**\n\n- automated focus stacking (though it is not documented at all), when using the openscan firmware\n- ~0.5TB of Data per month\n- minor issues with data transmission\n- hard limitation to 2GB max filesize\n- manual access token creation and user management\n- missing documentation\n- rudimentary windows uploader and python script\n\n___\n### OpenScanBenchy\n\nAn approach to create a recognizable benchmark for small object 3d scanners. Details on [github](https://github.com/OpenScanEu/OpenScanBenchy/tree/main)\n\n___\n___\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "# OpenScan2 - 3D Scanner\n## Related and more specific repositories\n\nIf you want to take part in the development of a specific part of the OpenScan system, feel free to join:\n\n* [OpenScanCloud - Web API for photogrammetry processing of image files](https://github.com/OpenScanEu/OpenScanCloud)\n* [OpenScan-Design - 3D printable files and other design approaches](https://github.com/OpenScanEu/OpenScan-Design)\n* [OpenScan-PCB - A place to discuss and improve the PCB designs](https://github.com/OpenScanEu/OpenScan-PCB)\n* [OpenScan-ML - Development of new tools using Machine Learning](https://github.com/OpenScanEu/OpenScan-ML)\n\n## Contribution and contributors\n\nThe project is based on the contribution of many great and open-minded people by doing tutorials on Youtube, comments on Reddit, publications on GitHub and many other places. Without all those voluntary contributors, this project would not be possible at all. Please feel free to join the discussions and development preferably in this repository or on [r/OpenScan](https://www.reddit.com/r/OpenScan/), [Facebook - LowBudget3DScan](https://www.facebook.com/groups/142108429832711) or [OpenScan.eu/forum](https://openscan.eu/forum)\n\nThank you!\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: OpenScan\nplugins:\n  - autolinks\n  - search:\n      lang:\n        - en\ntheme:\n  name: material\n  language: en\n  palette:\n    primary: teal\n    accent: indigo\n  features:\n    - content.code.annotate\n    - search.suggest\n    - search.highlight\nmarkdown_extensions:\n  - pymdownx.highlight:\n      anchor_linenums: true\n  - pymdownx.inlinehilite\n  - pymdownx.snippets\n  - pymdownx.emoji:\n      emoji_index: !!python/name:materialx.emoji.twemoji\n      emoji_generator: !!python/name:materialx.emoji.to_svg\n  - attr_list\n  - md_in_html\n  - admonition\n  - pymdownx.details\n  - pymdownx.superfences\n  - pymdownx.tabbed:\n      alternate_style: true\n  - pymdownx.critic\n  - pymdownx.caret\n  - pymdownx.keys\n  - pymdownx.mark\n  - pymdownx.tilde\n  - def_list\n  - pymdownx.tasklist:\n      custom_checkbox: true\n\nnav:\n  - Introduction: \"index.md\"\n  - Builds:\n    - Classic: \"builds/OpenScanClassic.md\"\n    - Mini: \"builds/OpenScanMini.md\"\n    - PCB: \"builds/PCBs.md\"\n  - Firmware:\n    - Setup: \"firmware/setup.md\"\n    - Usage: \"firmware/usage.md\"\n  - Photogrammetry:\n    - Basics: \"photogrammetry/basics.md\"\n    - Software: \"photogrammetry/software.md\"\n  - Changelog: \"changelog.md\"\n"
  },
  {
    "path": "software_dev_outline.md",
    "content": "Based on [OpenScan3](https://github.com/OpenScan-org/OpenScan3)\n\n## 1. Core Infrastructure & Setup\n\n- [ ] Update initial setup procedure\n- [x] Basic FastAPI application structure setup (already done in app-main.py)\n- [ ] Configure settings for different hardware setups (greenshield, blackshield, grblHAL)\n- [ ] Set up error handling and logging system\n- [ ] Implement Network security\n- [ ] Use virtual environment\n- [ ] Integrate ramdisk for faster temporary file handling\n\n### 1.1. Network & Connectivity\n- [ ] Add hotspot mode\n- [ ] Add wifi configuration & testing\n- [ ] Add test for internet connectivity\n\n## 2. Hardware Control Components\n\n### 2.1. Camera System \n\n- [ ] Review and update camera controllers (gphoto2, v4l2, picamera2)\n- [ ] Implement unified camera interface\n- [ ] Implement camera settings management\n- [ ] Integrate Focus control system:\n  - [ ] Integrate software controlled focus\n  - [ ] Add mechanic focus through third motor\n\n### 2.2. Motor Control System\n\n- [ ] Migrate motor controllers from old system (directly through GPIO)\n- [ ] Add Motor controller through GRBLhal\n- [ ] Integrate optional endstops\n- [ ] Implement motor movement coordination\n- [ ] Add tests for users\n\n### 2.3. Light Control system\n\n- [ ] Migrate Light controller (directly through GPIO)\n- [ ] Add Light controller through GRBLhal\n- [ ] Add tests for users\n\n### 2.4. Fan Control System\n- [ ] Add fan controller (directly through GPIO)\n- [ ] Add Fan controller through GRBLhal\n- [ ] Add Temperature dependent fan control\n\n### 2.5. Other Peripherals ?\n\n## 3. Scanning Logic\n\n### 3.1 Smart Pre-Scan systems (nice to have/optional)\n\n- [ ] Add auto-exposure detection based on histogram\n- [ ] Add auto-crop detection routine\n- [ ] Add auto-depth detection\n- [ ] Add Evaluation of object preparation (feature detection)\n\n### 3.2. Core Scanning System\n\n- [ ] Add scan templates/presets\n- [ ] Migrate scanning process controller\n- [ ] Implement proper scan state management\n  - [ ] Add scan progress tracking\n  - [ ] Add resume from failure point\n  - [ ] Implement pause/resume functionality\n\n### 3.3. Path Generation\n\n- [ ] Migrate different scanning patterns (Grid, Fibonacci, Spiral, Archimedes)\n- [ ] Implement path optimization\n- [ ] Add path visualization support\n\n### 4. Scan Project Handling\n\n- [ ] Implement project creation and management\n- [ ] Implement proper file structure for projects\n- [ ] Add External drive for saving\n- [ ] Add network drive for saving\n- [ ] Add project metadata handling\n- [ ] Add download project\n- [ ] Add delete project\n- [ ] Add delete all projects\n- [ ] Add merge projects\n- [ ] Add scan meta data (positions, focus, resolution, timestamps)\n\n## 5. Processing Integration\n\n### 5.1. OpenScan Cloud\n\n- [ ] Migrate cloud upload functionality\n- [ ] Implement secure authentication\n- [ ] Add progress tracking for uploads\n- [ ] Implement download functionality\n\n### 5.2. create Project files for other programs\n- [ ] Metashape\n- [ ] Reality Capture\n- [ ] 3DF Zephyr\n- [ ] Meshroom\n\n## 6. System Services\n\n- [ ] Migrate system status monitoring\n- [ ] Implement proper shutdown/reboot handlers\n- [ ] Add system health checks\n- [ ] Add system statistics\n- [ ] Add Diskspace monitoring\n- [ ] Add Diskspace warnings\n- [ ] Implement update service\n- [ ] Implement update versioning\n- [ ] Add Change Log\n- [ ] Add Server message service\n- [ ] Add Samba client\n- [ ] Add SSH on/off\n\n\n## 7. Testing ??\n- [ ] Set up unit testing infrastructure\n- [ ] Add integration tests\n- [ ] Implement hardware simulation for testing\n- [ ] Add CI/CD pipeline\n\n\n## 8. Documentation\n\n- [ ] API documentation\n- [ ] System architecture documentation\n- [ ] Hardware setup documentation\n- [ ] User guide\n\n\n"
  },
  {
    "path": "update/beta/Arducam.py",
    "content": "import time\nimport os\n\ntry:\n    import v4l2\nexcept Exception as e:\n    print(e)\n    print(\"Try to install v4l2-fix\")\n    try:\n        from pip import main as pipmain\n    except ImportError:\n        from pip._internal import main as pipmain\n    pipmain(['install', 'v4l2-fix'])\n    print(\"\\nTry to run the focus program again.\")\n    exit(0)\n\nimport fcntl\nimport errno\n\n# # Type\n# v4l2.V4L2_CTRL_TYPE_INTEGER\n# v4l2.V4L2_CTRL_TYPE_BOOLEAN\n# v4l2.V4L2_CTRL_TYPE_MENU\n# v4l2.V4L2_CTRL_TYPE_BUTTON\n# v4l2.V4L2_CTRL_TYPE_INTEGER64\n# v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n# # Flags\n# v4l2.V4L2_CTRL_FLAG_DISABLED\n# v4l2.V4L2_CTRL_FLAG_GRABBED\n# v4l2.V4L2_CTRL_FLAG_READ_ONLY\n# v4l2.V4L2_CTRL_FLAG_UPDATE\n# v4l2.V4L2_CTRL_FLAG_INACTIVE\n# v4l2.V4L2_CTRL_FLAG_SLIDER\n\ndef assert_valid_queryctrl(queryctrl):\n    return queryctrl.type & (\n        v4l2.V4L2_CTRL_TYPE_INTEGER\n        | v4l2.V4L2_CTRL_TYPE_BOOLEAN\n        | v4l2.V4L2_CTRL_TYPE_MENU\n        | v4l2.V4L2_CTRL_TYPE_BUTTON\n        | v4l2.V4L2_CTRL_TYPE_INTEGER64\n        | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n        | 7\n        | 8\n        | 9\n    ) and queryctrl.flags & (\n        v4l2.V4L2_CTRL_FLAG_DISABLED\n        | v4l2.V4L2_CTRL_FLAG_GRABBED\n        | v4l2.V4L2_CTRL_FLAG_READ_ONLY\n        | v4l2.V4L2_CTRL_FLAG_UPDATE\n        | v4l2.V4L2_CTRL_FLAG_INACTIVE\n        | v4l2.V4L2_CTRL_FLAG_SLIDER\n    )\n\ndef get_device_controls_menu(fd, queryctrl):\n    querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum)\n    while querymenu.index <= queryctrl.maximum:\n        fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu)\n        yield querymenu\n        querymenu.index += 1\n\ndef get_device_controls_by_class(fd, control_class):\n    # enumeration by control class\n    queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n    while True:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n        except IOError as e:\n            assert e.errno == errno.EINVAL\n            break\n        if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class:\n            break\n        yield queryctrl\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n\ndef getdict(struct):\n    val = dict((field, getattr(struct, field)) for field, _ in struct._fields_)\n    val.pop(\"reserved\")\n    return val\n\ndef get_device_controls(fd):\n    # original enumeration method\n    queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE)\n    while queryctrl.id < v4l2.V4L2_CID_LASTP1:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n            print(queryctrl.name)\n        except IOError as e:\n            # this predefined control is not supported by this device\n            assert e.errno == errno.EINVAL\n            queryctrl.id += 1\n            continue\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1)\n\ndef get_ctrls(vd):\n    ctrls = []\n    # enumeration by control class\n    for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA):\n        for queryctrl in get_device_controls_by_class(vd, class_):\n            ctrl = getdict(queryctrl)\n            if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    # print(querymenu.name)\n                    ctrl[\"menu\"].append(querymenu.name)\n\n            if queryctrl.type == 9:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    ctrl[\"menu\"].append(querymenu.index)\n            ctrls.append(ctrl)\n    return ctrls\n\ndef set_ctrl(vd, id, value):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    ctrl.value = value\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n\ndef get_ctrl(vd, id):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n        return None\n    return ctrl.value\n\n\nclass Focuser:\n    FOCUS_ID = 0x009a090a\n    dev = None\n\n    def __init__(self, dev=0):\n        self.focus_value = 0\n        self.dev = dev\n\n        if type(dev) == int or (type(dev) == str and dev.isnumeric()):\n            self.dev = \"/dev/video{}\".format(dev)\n\n        self.fd = open(self.dev, 'r')\n        self.ctrls = get_ctrls(self.fd)\n        self.hasFocus = False\n        for ctrl in self.ctrls:\n            if ctrl['id'] == Focuser.FOCUS_ID:\n                self.hasFocus = True\n                self.opts[Focuser.OPT_FOCUS][\"MIN_VALUE\"] = ctrl['minimum']\n                self.opts[Focuser.OPT_FOCUS][\"MAX_VALUE\"] = ctrl['maximum']\n                self.opts[Focuser.OPT_FOCUS][\"DEF_VALUE\"] = ctrl['default']\n                self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID)\n\n        if not self.hasFocus:\n            raise RuntimeError(\"Device {} has no focus_absolute control.\".format(self.dev))\n\n    def read(self):\n        return self.focus_value\n\n    def write(self, value):\n        self.focus_value = value\n        # os.system(\"v4l2-ctl -d {} -c focus_absolute={}\".format(self.dev, value))\n        set_ctrl(self.fd, Focuser.FOCUS_ID, value)\n\n    OPT_BASE    = 0x1000\n    OPT_FOCUS   = OPT_BASE | 0x01\n    OPT_ZOOM    = OPT_BASE | 0x02\n    OPT_MOTOR_X = OPT_BASE | 0x03\n    OPT_MOTOR_Y = OPT_BASE | 0x04\n    OPT_IRCUT   = OPT_BASE | 0x05\n    opts = {\n        OPT_FOCUS : {\n            \"MIN_VALUE\": 0,\n            \"MAX_VALUE\": 1000,\n            \"DEF_VALUE\": 0,\n        },\n    }\n    def reset(self,opt,flag = 1):\n        info = self.opts[opt]\n        if info == None or info[\"DEF_VALUE\"] == None:\n            return\n        self.set(opt,info[\"DEF_VALUE\"])\n\n    def get(self,opt,flag = 0):\n        info = self.opts[opt]\n        return self.read()\n\n    def set(self,opt,value,flag = 1):\n        info = self.opts[opt]\n        if value > info[\"MAX_VALUE\"]:\n            value = info[\"MAX_VALUE\"]\n        elif value < info[\"MIN_VALUE\"]:\n            value = info[\"MIN_VALUE\"]\n        self.write(value)\n        print(\"write: {}\".format(value))\n\n    def __del__(self):\n        self.fd.close()\n\npass\n"
  },
  {
    "path": "update/beta/OpenScan.py",
    "content": "basepath = '/home/pi/OpenScan/'\nfrom os.path import isfile\n\ndef load_bool(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    if value == '1' or value == 'True' or value =='true':\n        value = True\n    else:\n        value = False\n    return value\n\ndef load_str(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    return value\n\ndef load_int(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = int(file.read().replace('\\n',''))\n    return value\n\ndef load_float(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = float(file.read().replace('\\n',''))\n    return value\n\ndef save(name, value):\n    filename = basepath+'settings/'+name\n    with open(filename, 'w+') as file:\n        file.write(str(value))\n    return\n\ndef OpenScanCloud(cmd, msg):\n    from requests import get\n    osc_user = 'openscan'\n    osc_pw = 'free'\n    osc_server = 'http://openscanfeedback.dnsuser.de:1334/'\n\n    try:\n        r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg)\n    except:\n        r = type('obj', (object,), {'status_code' : 404, 'text':None})\n    return r\n\ndef camera(cmd, msg = {}):\n    from requests import get\n    flask = 'http://127.0.0.1:1312/'\n    try:\n        r = get(flask + cmd, params=msg)\n        return r.status_code\n    except:\n        return 400\n\ndef motorrun(motor,angle,ES_enable=False,ES_start_state = True):\n    #motor can be \"rotor\", \"tt\" or \"extra\"\n    import RPi.GPIO as GPIO\n    from time import sleep\n    from math import cos\n    msg = {'cmd':'set'}\n    camera('/ping', msg)\n\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n\n    spr = load_int(motor + '_stepsperrotation')\n    dirpin = load_int('pin_' + motor + '_dir')\n    steppin = load_int('pin_' + motor +'_step')\n    ES_pin = load_int('pin_' + motor + '_endstop')\n    dir = load_int(motor + '_dir')\n    ramp = load_int(motor + '_accramp')\n    acc = load_float(motor + '_acc')\n    delay_init = load_float(motor + '_delay')\n    delay = delay_init\n\n    step_count=int(angle*spr/360) * dir\n    GPIO.setup(dirpin, GPIO.OUT)\n    GPIO.setup(steppin, GPIO.OUT)\n    GPIO.setup(ES_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP)\n\n    if (step_count>0):\n        GPIO.output(dirpin, GPIO.HIGH)\n    if(step_count<0):\n        GPIO.output(dirpin, GPIO.LOW)\n        step_count=-step_count\n    for x in range(step_count):\n        if ES_enable == True and GPIO.input(ES_pin) != ES_start_state:\n            break\n        GPIO.output(steppin, GPIO.HIGH)\n        if x<=ramp and x<=step_count/2:\n            delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc)\n            #delay=delay_init+(ramp-x)*(delay_init)/acc\n        elif step_count-x<=ramp and x>step_count/2:\n            delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc)\n            #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc\n        else:\n            delay = delay_init\n        sleep(delay)\n        GPIO.output(steppin, GPIO.LOW)\n        sleep(delay)\n\ndef ringlight(number,state):\n    import RPi.GPIO as GPIO\n    msg = {'cmd':'set'}\n    camera('/ping', msg)\n    pin = load_int('pin_ringlight' + str(number))\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, state)\n\ndef take_photo(file):\n    from os import system\n    filepath = basepath + file\n\n    model=load_str('model')\n\n\n\n    shutter = str(load_int('cam_shutter'))\n    saturation = load_str('cam_saturation')\n    contrast = load_str('cam_contrast')\n    awbg_red = load_str('cam_awbg_red')\n    awbg_blue = load_str('cam_awbg_blue')\n    gain = load_str('cam_gain')\n    quality = load_int('cam_jpeg_quality')\n    filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg'\n    #width = load_str('cam_resx')\n    #height = load_str('cam_resy')\n    timeout = load_str('cam_timeout')\n    cropx = load_int('cam_cropx')/200\n    cropy = load_int('cam_cropy')/200\n    rotation = load_int('cam_rotation')\n    AF = load_bool('cam_AFmode')\n    camera = load_str('camera')\n\n\n    if camera == 'imx519' and AF == True:\n        autofocus = ' --autofocus '\n    else:\n        autofocus = ''\n\n    if camera  == \"usb_webcam\":\n        cmd = 'fswebcam -i 0 -r \"1280x720\" -F 5 --no-banner --jpeg 95 --save ' + filepath2\n    else:\n        cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1'\n    #    cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus\n        \n    system(cmd)\n    return cmd\n\ndef get_points(samples=1):\n    from math import pi, sqrt, acos, atan2, cos, sin\n\n    points = []\n    phi = pi * (3. - sqrt(5.))\n    for i in range(int(samples)):\n        y = 1 - (i / float(samples - 1)) * 2\n        radius = sqrt(1 - y * y)\n        theta = phi * i\n        x = cos(theta) * radius\n        z = sin(theta) * radius\n        r=sqrt(x*x+y*y+z*z)\n        theta_neu=acos(z/r)*180/pi\n        phi_neu=atan2(y,x)*180/pi\n        points.append((theta_neu-90,phi_neu))\n    points.sort()\n    return points\n\ndef create_coordinates(angle_min, angle_max,point_count):\n    point_count_final=point_count\n    if angle_max < angle_min:\n        a = angle_min\n        angle_min = angle_max\n        angle_max = a\n    point_count=point_count*90/(angle_max-angle_min)\n    actual_points=0\n    while actual_points<point_count_final:\n        points=get_points(point_count)\n        filtered=[]\n        for x,y in points:\n            if x>angle_min and x<angle_max and len(filtered)<point_count_final:\n                filtered.append((x,y))\n        actual_points=len(filtered)\n\n        if point_count-actual_points>20:\n            point_count=point_count+3\n        else:\n            point_count=point_count+1\n    return filtered\n\n"
  },
  {
    "path": "update/beta/config.txt",
    "content": "# For more options and information see\n# http://rpf.io/configtxt\n# Some settings may impact device functionality. See link above for details\n\n\n# uncomment if you get no picture on HDMI for a default \"safe\" mode\n#hdmi_safe=1\nhdmi_blanking=2\n\n# uncomment the following to adjust overscan. Use positive numbers if console\n# goes off screen, and negative if there is too much border\n#overscan_left=16\n#overscan_right=16\n#overscan_top=16\n#overscan_bottom=16\n\n# uncomment to force a console size. By default it will be display's size minus\n# overscan.\n#framebuffer_width=1280\n#framebuffer_height=720\n\n# uncomment if hdmi display is not detected and composite is being output\n#hdmi_force_hotplug=1\n\n# uncomment to force a specific HDMI mode (this will force VGA)\n#hdmi_group=1\n#hdmi_mode=1\n\n# uncomment to force a HDMI mode rather than DVI. This can make audio work in\n# DMT (computer monitor) modes\n#hdmi_drive=2\n\n# uncomment to increase signal to HDMI, if you have interference, blanking, or\n# no display\n#config_hdmi_boost=4\n\n# uncomment for composite PAL\n#sdtv_mode=2\n\n#uncomment to overclock the arm. 700 MHz is the default.\n#arm_freq=800\n\n# Uncomment some or all of these to enable the optional hardware interfaces\n#dtparam=i2c_arm=on\n#dtparam=i2s=on\n#dtparam=spi=on\n\n# Uncomment this to enable infrared communication.\n#dtoverlay=gpio-ir,gpio_pin=17\n#dtoverlay=gpio-ir-tx,gpio_pin=18\n\n# Additional overlays and parameters are documented /boot/overlays/README\n\n# Enable audio (loads snd_bcm2835)\ndtparam=audio=on\n\n# Automatically load overlays for detected cameras\ncamera_auto_detect=0\n\n# Automatically load overlays for detected DSI displays\ndisplay_auto_detect=1\n\n# Enable DRM VC4 V3D driver\n#dtoverlay=vc4-kms-v3d\nmax_framebuffers=2\n\n# Disable compensation for displays with overscan\ndisable_overscan=1\n\n[cm4]\n# Enable host mode on the 2711 built-in XHCI USB controller.\n# This line should be removed if the legacy DWC2 controller is required\n# (e.g. for USB device mode) or if USB support is not required.\notg_mode=1\n\n[pi4]\n# Run as fast as firmware / board allows\narm_boost=1\n\n[all]\n\ncamera_auto_detect=0\ngpu_mem=256\ndtoverlay=vc4-fkms-v3d\ndtoverlay=imx519,media-controller=1\n"
  },
  {
    "path": "update/beta/fla.py",
    "content": "from flask import Flask, make_response, jsonify, request, abort\nfrom PIL import Image\nimport gphoto2 as gp\nfrom time import sleep, time\nimport shutil\nfrom OpenScan import load_int, load_float, load_bool, ringlight\nimport RPi.GPIO as GPIO\nfrom math import sqrt\nimport os \n\nGPIO.setwarnings(False)\nGPIO.setmode(GPIO.BCM)\n\napp = Flask(__name__)\n\nbasedir = '/home/pi/OpenScan/'\ntimer = time()\n\n###################################################################################################################\n@app.route('/shutdown', methods=['get'])\ndef shutdown():\n    delay = 0.1\n    ringlight(2,False)\n\n    for i in range (5):\n        ringlight(1,True)\n        sleep(delay)\n        ringlight(1,False)\n        sleep(delay)\n    os.system('shutdown -h now')\n###################################################################################################################\n@app.route('/reboot', methods=['get'])\ndef reboot():\n    delay = 0.1\n    ringlight(2,False)\n\n    for i in range (5):\n        ringlight(1,True)\n        sleep(delay)\n        ringlight(1,False)\n        sleep(delay)\n\n    os.system('reboot -h')\n###################################################################################################################\n@app.route('/ping', methods=['get'])\ndef ping():\n    global timer\n    cmd = str(request.args.get('cmd'))\n    if cmd == 'set':\n        timer = time()\n    inactive = time() - timer\n    return ({'inactive':inactive}, 200)\n###################################################################################################################\n@app.route('/gphoto_init', methods=['get'])\ndef gphoto_init():\n    global camera\n    camera = gp.Camera()\n    camera.init()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_preview', methods=['get'])\ndef gphoto_preview():\n    filepath = str(request.args.get('filepath'))\n    camera_file = gp.gp_camera_capture_preview(camera)[1]\n    target = basedir + filepath\n    camera_file.save(target)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_capture', methods=['get'])\ndef gphoto_capture():\n    filepath = str(request.args.get('filepath'))\n    file_path = camera.capture(gp.GP_CAPTURE_IMAGE)\n    camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL)\n    camera_file.save(basedir + filepath)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_test', methods=['get'])\ndef gphoto_test():\n    text = camera.get_summary()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_exit', methods=['get'])\ndef gphoto_exit():\n    global camera\n    camera.exit()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/crop', methods=['get'])\ndef crop():\n    output_downscale = load_bool('cam_output_downscale')\n    output_resolution = load_int('cam_output_resolution')\n    preview_resolution = load_int('cam_preview_resolution')\n    filepath_in = basedir + str(request.args.get('filepath_in'))\n    filepath_out = basedir + str(request.args.get('filepath_out'))\n    cropx = int(request.args.get('cropx'))/200\n    cropy = int(request.args.get('cropy'))/200\n    rotation = int(request.args.get('rotation'))\n    preview = str(request.args.get('preview'))\n    downscale = 1\n\n    with Image.open(filepath_in) as img:\n        w,h = img.size\n        if cropx != 0 or cropy != 0:\n            img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy)))\n        if rotation == 90:\n            img  = img.transpose(Image.ROTATE_90)\n        elif rotation == 180:\n            img= img.transpose(Image.ROTATE_180)\n        elif rotation == 270:\n            img= img.transpose(Image.ROTATE_270)\n\n        if preview == \"True\":\n            w,h = img.size\n            factor = (w*h)/preview_resolution\n            if factor > 1:\n                img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS)\n\n        elif output_downscale == True:\n            w,h = img.size\n            factor = (w*h)/output_resolution\n            if factor > 1:\n                img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) \n\n        img.save(filepath_out, quality=95, subsampling=0)\n\n    return ({}, 200)\n\n###################################################################################################################\n@app.route('/external_capture', methods=['get'])\ndef external_capture():\n    pin = load_int('pin_external')\n    delay_before = load_float('cam_delay_before')\n    timeout = load_float('cam_timeout')/1000\n    delay_after = load_float('cam_delay_after')\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_before)\n    GPIO.output(pin, GPIO.HIGH)\n    sleep(timeout)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_after)\n    return ({}, 200)\n\n\n\n\nif __name__ == '__main__':\n#    app.run(host='127.0.0.1', port=1312, debug=False, threaded=True)\n    app.run(host='0.0.0.0', port=1312, debug=False, threaded=True)\n"
  },
  {
    "path": "update/beta/flows.json",
    "content": "[\n    {\n        \"id\": \"829d803b6033a693\",\n        \"type\": \"tab\",\n        \"label\": \"HOME\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"1613373abaf77a2c\",\n        \"type\": \"tab\",\n        \"label\": \"SCAN\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"4981d84ef1a366d1\",\n        \"type\": \"tab\",\n        \"label\": \"Files&Cloud\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"017bd4e4a428bee5\",\n        \"type\": \"tab\",\n        \"label\": \"SETTINGS\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"c8e7ecb5849edb9a\",\n        \"type\": \"tab\",\n        \"label\": \"UPDATE\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"b3150b13e34b1fe8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"OpenScan\",\n        \"icon\": \"dashboard\",\n        \"order\": 1,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"b6e9c2df6b28ff66\",\n        \"type\": \"ui_base\",\n        \"theme\": {\n            \"name\": \"theme-dark\",\n            \"lightTheme\": {\n                \"default\": \"#0094CE\",\n                \"baseColor\": \"#0094CE\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"darkTheme\": {\n                \"default\": \"#097479\",\n                \"baseColor\": \"#097479\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"customTheme\": {\n                \"name\": \"Untitled Theme 1\",\n                \"default\": \"#4B7930\",\n                \"baseColor\": \"#4B7930\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n            },\n            \"themeState\": {\n                \"base-color\": {\n                    \"default\": \"#097479\",\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-titlebar-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-backgroundColor\": {\n                    \"value\": \"#111111\",\n                    \"edited\": false\n                },\n                \"page-sidebar-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"group-textColor\": {\n                    \"value\": \"#0eb8c0\",\n                    \"edited\": false\n                },\n                \"group-borderColor\": {\n                    \"value\": \"#555555\",\n                    \"edited\": false\n                },\n                \"group-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"widget-textColor\": {\n                    \"value\": \"#eeeeee\",\n                    \"edited\": false\n                },\n                \"widget-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"widget-borderColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"base-font\": {\n                    \"value\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n                }\n            },\n            \"angularTheme\": {\n                \"primary\": \"indigo\",\n                \"accents\": \"blue\",\n                \"warn\": \"red\",\n                \"background\": \"grey\",\n                \"palette\": \"light\"\n            }\n        },\n        \"site\": {\n            \"name\": \"OpenScan 3D Scanner\",\n            \"hideToolbar\": \"false\",\n            \"allowSwipe\": \"false\",\n            \"lockMenu\": \"false\",\n            \"allowTempTheme\": \"true\",\n            \"dateFormat\": \"DD/MM/YYYY\",\n            \"sizes\": {\n                \"sx\": 46,\n                \"sy\": 46,\n                \"gx\": 10,\n                \"gy\": 10,\n                \"cx\": 6,\n                \"cy\": 6,\n                \"px\": 6,\n                \"py\": 6\n            }\n        }\n    },\n    {\n        \"id\": \"729f9ea6e3513c9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Home\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"65ae49b64fa0d83e\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Settings\",\n        \"icon\": \"dashboard\",\n        \"order\": 4,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"4fe6b4c0ade0938a\",\n        \"type\": \"ui_group\",\n        \"name\": \"General\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"0fe66c9190b8a87c\",\n        \"type\": \"ui_group\",\n        \"name\": \"Network\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"93aadb71dee6d977\",\n        \"type\": \"ui_group\",\n        \"name\": \"Camera\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 4,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d49a6dfd7fb17096\",\n        \"type\": \"ui_group\",\n        \"name\": \"Motor\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 5,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"644b3bcc903d46ca\",\n        \"type\": \"ui_group\",\n        \"name\": \"Pinout\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 6,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"e23b837a9f040895\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Scan\",\n        \"icon\": \"dashboard\",\n        \"order\": 2,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"7aaf184330605300\",\n        \"type\": \"ui_group\",\n        \"name\": \"Settings\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"ce9cc9d915dc6eb6\",\n        \"type\": \"ui_group\",\n        \"name\": \"Picamera\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"12\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"90223f7ddc082321\",\n        \"type\": \"ui_group\",\n        \"name\": \"Arducam\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": 12,\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"7625f9c9e8dbc5c6\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3b4bd36726be16d5\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 3,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"b5fdd57b.15eda8\",\n        \"type\": \"ui_group\",\n        \"name\": \"Main\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": 13,\n        \"collapse\": false\n    },\n    {\n        \"id\": \"db43d646.2074c8\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"15a222ed.d70a7d\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Files&Cloud\",\n        \"icon\": \"dashboard\",\n        \"order\": 3,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"ddbd496e.93a288\",\n        \"type\": \"ui_group\",\n        \"name\": \"Manage Updates\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"3ce32450.e0cffc\",\n        \"type\": \"ui_group\",\n        \"name\": \"System & Stats\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d25e08b4.5b27e8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Update & Info\",\n        \"icon\": \"dashboard\",\n        \"order\": 5,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"1f7f7e1e24f5ad9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Initialize\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"5b3e5aca21140e9a\",\n        \"type\": \"ui_group\",\n        \"name\": \"Update\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"700f47327133ab68\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"spacer\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 6,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"ebf828f29201a53b\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"spacer\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 8,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3b4961c4e72ff58a\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"5ef40dca2c6c6aab\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 11,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"bdd26746cc1e1ba0\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3584b5ef2b7acb72\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"cac67f0e.f01fa\",\n        \"type\": \"ui_group\",\n        \"name\": \"Button Top\",\n        \"tab\": \"\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"b73c392ffd8ca3f2\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 14,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"89fe04171cd2f35b\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 15,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"80c9c0059de08f02\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 16,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3fe52603e2ac73b6\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"name\": \"Background\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"format\": \"<style>\\n    body {\\n        background-image: url(\\\"/files/logo.jpg\\\");\\n        background-repeat: no-repeat;\\n        background-attachment: fixed;\\n        background-position: center;\\n\\n    }\\n    #OpenScan_Home {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n    [node-id=\\\"a291fc98e4269c1b\\\"] {\\n    background-color: transparent !important;\\n    }\\n\\n    \\n    \\n    #OpenScan_Update {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n</style>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 110,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4468f691.103eb8\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"SCAN\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"1\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6560dd25.9e76c4\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Settings\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"3\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd5288.2805fc\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"events\": \"all\",\n        \"x\": 280,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"71e72293.91c6fc\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Files\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"2\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e7306ef2.3b4df\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Update&Info\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"4\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"88edad7ca53698fd\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"x\": 90,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"000a811a215e08d4\",\n                \"83c2b5ea51f0fec3\",\n                \"88fde4ab78c965d7\",\n                \"bee62d2a99cbc63b\",\n                \"8e39e4a037487ecd\",\n                \"bb84b9e5c7d8e21f\",\n                \"7113d7b25a851151\",\n                \"c4c1580c289fc7bd\",\n                \"7494af94bc76a6a8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bd75f33b8a57c522\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"92c98e6ce7cd25f9\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 335,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"000a811a215e08d4\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"func\": \"msg.enabled = true\\nmsg.payload = 1\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"bd75f33b8a57c522\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"83c2b5ea51f0fec3\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 240,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"6b94bf2295b1b31d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6b94bf2295b1b31d\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\"\n        ],\n        \"x\": 335,\n        \"y\": 480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"88fde4ab78c965d7\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_cloud'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = 'ready'\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"960912e90ba5b5bc\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\"\n        ],\n        \"x\": 615,\n        \"y\": 800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"168d72a54504b327\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"5/0.1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"5\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"x\": 100,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"6c6ef2255a7d39e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6c6ef2255a7d39e5\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"repeat 5s/0.1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"61990987acd0f263\",\n            \"2415272f42ce468c\"\n        ],\n        \"x\": 195,\n        \"y\": 720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bee62d2a99cbc63b\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"global\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nglobal.set('combine', false)\\nglobal.set('focus', 2838)\\nglobal.set('focus1', 0)\\nglobal.set('focus2', 0)\\n\\nglobal.set('focuser', true)\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"f20da2fc4978b7bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"544d20f02215011a\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"CREATE FACTORY DEFAULT\",\n        \"func\": \"msg = { \\n'overwrite':msg.overwrite,\\n'settings':\\n    {\\n    'advanced_settings':false,\\n    'cameras':{\\n        'imx519':[4656,3496],\\n        'imx219':[3280,2464],\\n        'imx477':[4056,3040],\\n        'ov5647':[2592,1944],\\n        'imx378':[3840,2880],\\n        'ov9271':[1280,800],\\n        'imx290a':[1920,1080],\\n        'imx290b':[1920,1080],\\n    },\\n    'cam_AFmode':true,\\n    'cam_STmode':true,\\n    'cam_stacksize':2,\\n    'cam_awbg_blue':0,\\n    'cam_awbg_red':0,\\n    'cam_contrast':1,\\n    'cam_cropx':0,\\n    'cam_cropy':0,\\n    'cam_delay_after':0,\\n    'cam_delay_before':0,\\n    'camera':'',\\n    'cam_gain':1,\\n    'cam_jpeg_quality':95,\\n    'cam_rotation':0,\\n    'cam_saturation':1,\\n    'cam_shutter':50000,\\n    'cam_timeout':800,\\n    'hostname':'',\\n    'model':'',\\n    'osc_credit':'',\\n    'osc_limit_filesize':'',\\n    'osc_limit_photos':'',\\n    'osc_splitsize':200000000,\\n    'pin_endstop1':24,\\n    'pin_endstop2':25,\\n    'pin_external':10,\\n    'pin_ringlight1':17,\\n    'pin_ringlight2':27,\\n    'pin_rotor_dir':5,\\n    'pin_rotor_enable':23,\\n    'pin_rotor_step':6,\\n    'pin_tt_dir':9,\\n    'pin_tt_enable':22,\\n    'pin_tt_step':11,\\n    'rotor_acc':1,\\n    'rotor_accramp':2000,\\n    'rotor_angle':10,\\n    'rotor_anglemax':60,\\n    'rotor_anglemin':-20,\\n    'rotor_anglestart':0,\\n    'rotor_delay':0.0001,\\n    'rotor_dir':1,\\n    'rotor_stepsperrotation':48000,\\n    'routine_photocount':50,\\n    'routine_projectname':'default',\\n    'smb':true,\\n    'ssh':true,\\n    'status_cloud':'ready',\\n    'status_internal_cam':'--READY--',\\n    'status_uploadprogress':'',\\n    'terms':false,\\n    'token':'',\\n    'tt_acc':1,\\n    'tt_accramp':200,\\n    'tt_angle':10,\\n    'tt_delay':0.0001,\\n    'tt_dir':1,\\n    'tt_stepsperrotation':3200,\\n    'cam_focus':2838,\\n    'cam_focus1':0,\\n    'cam_focus2':0,\\n    'uploadprogress':'',\\n    'update_type':'beta',\\n    'update_auto':true,\\n    'downscale_threshold':1000,\\n    'turntable_mode':false,\\n    'timeout_ringlight':300,\\n    'diskspace_threshold':4000,\\n    'updateable':false,\\n    'cam_focuspeak':false,\\n    'cam_histogram':false,\\n    'routine_secondpass':true,\\n    'cam_output_resolution':20000000,\\n    'cam_preview_resolution':2000000,\\n    'cam_output_downscale':false,\\n    'pin_rotor_endstop':24,\\n    'pin_tt_endstop':25,\\n    'pin_extra_endstop':26,\\n    'pin_extra_dir':21,\\n    'pin_extra_step':20,\\n    'pin_extra_enable':19,\\n    'extra_acc':1,\\n    'extra_accramp':200,\\n    'extra_angle':10,\\n    'extra_delay':0.0001,\\n    'extra_dir':1,\\n    'extra_stepsperrotation':3200,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 310,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"c77552216a8bb781\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1f0ed7d5a9d670e\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 90,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c77552216a8bb781\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"chk files\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 520,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"960912e90ba5b5bc\",\n                \"ea0e57d83f291e23\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38783aea9cc317a6\",\n        \"type\": \"link in\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"factory reset\",\n        \"links\": [\n            \"80bccc884b0be297\",\n            \"beacc3dc5398fa79\"\n        ],\n        \"x\": 135,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f20da2fc4978b7bf\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"global\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"d14bbbb446d45e39\"\n        ],\n        \"x\": 345,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8e39e4a037487ecd\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"create log\",\n        \"func\": \"import subprocess\\nfrom time import sleep\\nsleep(20)\\n\\n\\nlog = '############################################DMESG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"dmesg\\\")\\nlog += '\\\\n############################################SYSLOG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"tail -10000 /var/log/syslog\\\")\\n\\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\\n    file.write(log)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 240,\n        \"y\": 560,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"be8cae9cf6f3585f\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"name\": \"first start\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 3,\n        \"format\": \"<h1 style=\\\"text-align: center;\\\">Initial Setup</h1>\\n<p style=\\\"text-align: center;\\\">Note, that you can always adjust these and other settings in the settings menu, which will appear after this setup stage.&nbsp;</p>\\n<p><strong>Model</strong></p>\\n<p style=\\\"margin-left: 20px;\\\">Please select the OpenScan Version - this will only affect the motor settings (acceleration, gear ratio, speed).</p>\\n<p><strong>Camera</strong></p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>Pi Camera v1, v2, HQ, Arducam IMX519, IMX290, IMX378, OV9281</u> are connected through the ribbon cable. If you encounter any issues, please check the cable&apos;s orientation</p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>DSLR (gphoto)</u> - can be used with a wide range of cameras, which can be connected and controlled via USB. Check <a href=\\\"http://www.gphoto.org/proj/libgphoto2/support.php\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">GPhoto</a> if your camera is supported</p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>External Camera</u> - Can be used to connect your camera trigger to the GPIO pins on the front of the pi shield. This can be used with any (modified) remote shutter release, and thus it is possible to use Smartphones, DSLR and compact cameras</p>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"8955d11554f55e63\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"5b3e5aca21140e9a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 3,\n        \"passthru\": false,\n        \"label\": \"Install Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 120,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"1e7457ea9c2c5e09\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1e7457ea9c2c5e09\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"update\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"39a502b38837273d\"\n        ],\n        \"x\": 245,\n        \"y\": 280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bb84b9e5c7d8e21f\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"rescue incomplete project\",\n        \"func\": \"#if project has not been done properly, this is a way to rescue the file\\n\\nfrom os import system\\nfrom os.path import isfile\\nfrom time import strftime\\nfrom OpenScan import load_str\\n\\nbasepath = '/home/pi/OpenScan/'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectname=load_str(\\\"routine_projectname\\\")\\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n\\nsystem('chmod -R 777 /home/pi/OpenScan/')\\n\\nif isfile(zippath):\\n    system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 600,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a291fc98e4269c1b\",\n        \"type\": \"ui_text\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 7,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"version\",\n        \"label\": \"Version:\",\n        \"format\": \"{{msg.firmware}}\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 460,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7113d7b25a851151\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"FIRMWARE VERSION\",\n        \"func\": \"msg.firmware = '2024-09-09'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"a291fc98e4269c1b\",\n                \"ec5cefa70ff535f7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ec5cefa70ff535f7\",\n        \"type\": \"ui_text\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"current version\",\n        \"label\": \"Current version:\",\n        \"format\": \"{{msg.firmware}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 320,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c4c1580c289fc7bd\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"create path\",\n        \"func\": \"import os\\n\\npaths = ['/home/pi/OpenScan/scans/preview/']\\n\\n\\nfor i in paths:\\n    if not os.path.isdir(i):\\n        os.mkdir(i)\",\n        \"outputs\": 1,\n        \"x\": 250,\n        \"y\": 640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"06d33bb8951ce668\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"\",\n        \"name\": \"donate\",\n        \"order\": 2,\n        \"width\": \"0\",\n        \"height\": \"0\",\n        \"format\": \"\\n<script id=\\\"titleScript\\\" type=\\\"text/javascript\\\">\\n\\n$(function() {\\n    if($('.md-toolbar-tools').length != 0){\\n        load();\\n    }else setTimeout(load, 500)\\n});\\n\\nfunction load(){\\n    $('#bmac').remove();\\n    var toolbar = $('.md-toolbar-tools');\\n    \\n    var div = $('<div/>');\\n    var p = $('<div/ id=\\\"bmac\\\">');\\n    \\n    div.append(p);\\n    div[0].style.margin = '0px 0px 0px auto';\\n    toolbar.append(div);\\n\\n\\n    p.append('<a class=\\\"md-button\\\" target=\\\"_blank\\\" href=\\\"https://www.patreon.com/bePatron?u=51974655\\\"><U>DONATE</U></a>');\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:0 5 0 5!important;\\\" href=\\\"http://openscan:1312/shutdown\\\"><i class=\\\"fa fa-power-off\\\" ></i></a>')\\n    p.append('   ')\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:10 0 10 0!important;\\\" href=\\\"http://openscan:1312/reboot\\\"><i class=\\\"fa fa-repeat\\\" ></i></a>')\\n\\n}\\n\\n</script>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"245e4341d4fb611c\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"pinmap_v2\",\n        \"func\": \"msg = { \\n'overwrite':true,\\n'settings':{\\n    'pin_rotor_endstop':27,\\n    'pin_tt_endstop':5,\\n    'pin_extra_endstop':26,\\n    'pin_external':25,\\n    'pin_ringlight1':24,\\n    'pin_ringlight2':24,\\n    'pin_rotor_dir':23,\\n    'pin_rotor_enable':19,\\n    'pin_rotor_step':22,\\n    'pin_tt_dir':6,\\n    'pin_tt_enable':19,\\n    'pin_tt_step':16,\\n    'pin_extra_dir':21,\\n    'pin_extra_step':20,\\n    'pin_extra_enable':19,\\n    'extra_acc':1,\\n    'extra_accramp':200,\\n    'extra_angle':10,\\n    'extra_delay':0.0001,\\n    'extra_dir':1,\\n    'extra_stepsperrotation':3200,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 870,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"627406f3611511dc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"627406f3611511dc\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"write\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 1010,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"50eeb3e362f9027f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"88b1bddde110298a\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 730,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"245e4341d4fb611c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50eeb3e362f9027f\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\"\n        ],\n        \"x\": 1095,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ea0e57d83f291e23\",\n        \"type\": \"debug\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"false\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 610,\n        \"y\": 860,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4f3121f158f06a61\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"Rotor left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\nfrom time import sleep\\n\\nmotorrun('rotor',100,True)\\n\\nmotorrun('tt',360,True)\\nmotorrun('extra',360,True)\",\n        \"outputs\": 1,\n        \"x\": 940,\n        \"y\": 80,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4a8a04b1e5dca8fe\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"run rotor till endstop\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 770,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"4f3121f158f06a61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7494af94bc76a6a8\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"fix_endstops\",\n        \"func\": \"from OpenScan import load_int, save\\n\\nif load_int('pin_rotor_endstop') == 27 and load_int('pin_tt_endstop') == 5:\\n    save('pin_rotor_endstop',24)\\n    save('pin_tt_endstop',25)\",\n        \"outputs\": 1,\n        \"x\": 250,\n        \"y\": 680,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"828e5298.d2192\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇐\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"b12e54fb.3141b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"96c7e241.458e6\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇒\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"37f52dd4.bd7572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e854876.6b6008\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇑\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"555aea34.b3b5e4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"753817f.1b9b3e8\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇓\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"9905e0c9.dddcd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8775044.3aa3ef8\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Turntable\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 100,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9e8a2d23.bf6ce\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Rotor\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"555aea34.b3b5e4\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"46e00b45.c24ca4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9905e0c9.dddcd\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"6ee089cb343a35ef\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b12e54fb.3141b8\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"c1871a2b9af5419a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"37f52dd4.bd7572\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"42b9f1fc49e69f54\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"46e00b45.c24ca4\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ee089cb343a35ef\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',-load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 370,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"42b9f1fc49e69f54\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',-load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 440,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c1871a2b9af5419a\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 350,\n        \"y\": 400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"aebad788761dce4a\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"routine_photocount\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"10\",\n        \"max\": \"300\",\n        \"step\": \"10\",\n        \"className\": \"\",\n        \"x\": 350,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"ce28a0b5bfb0d5a1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"107a030938cbfea9\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"aebad788761dce4a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ce28a0b5bfb0d5a1\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"84d6b96c8ebaac96\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"470b10726d298834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"470b10726d298834\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"shutter \",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"700\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"44c3947a9b92d32d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"44c3947a9b92d32d\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"069bcf58b1fe44cd\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"photocount\",\n        \"label\": \"Photos\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8dc7df1de59cb03a\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"shutter\",\n        \"label\": \"Shutter (ms)\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 650,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"cc69dba8d54a29dd\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Crop X\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"99\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"c2b2ab5524271123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e3a90602605fb9e9\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Crop Y\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"99\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"26f17a7f406df73c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9c6b48b7b4cc4e1a\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"cc69dba8d54a29dd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c470fd0b15356206\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"e3a90602605fb9e9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c2b2ab5524271123\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"26f17a7f406df73c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fecf5cff888bb570\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cropx\",\n        \"label\": \"{{msg.crop1}}\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 690,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0ee4950bd21498bd\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cropy\",\n        \"label\": \"{{msg.crop2}}\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 690,\n        \"y\": 660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ebbf11b55d758806\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 320,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"67385b196c517ac6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4b3112a9ec6c487\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload=\\\"default\\\"\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"ebbf11b55d758806\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67385b196c517ac6\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_projectname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload).replace(/ /g, '_')\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4dd7285c2b0fd79b\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"ringlight\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"all\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": 0,\n        \"max\": \"3\",\n        \"step\": 1,\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"873dace18a23fdf2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"873dace18a23fdf2\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"LED\",\n        \"func\": \"from OpenScan import ringlight\\nval = msg['payload']\\n\\nif val == 0:\\n    ringlight(1,False)\\n    ringlight(2,False)\\nelif val == 1:\\n    ringlight(1,False)\\n    ringlight(2,True)\\nelif val == 2:\\n    ringlight(1,True)\\n    ringlight(2,False)\\nelif val == 3:\\n    ringlight(1,True)\\n    ringlight(2,True)\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 700,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"9e30e33a1520fee0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"msg.payload = 0\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"4dd7285c2b0fd79b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dd287f40385922f\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start \",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 21,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-play\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"enabled\",\n        \"topicType\": \"str\",\n        \"x\": 150,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"33d94a04b96a2de0\",\n                \"6d15f717d5a11002\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"579f2211199fd6ab\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 23,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-stop\",\n        \"payload\": \"numberofphotos\",\n        \"payloadType\": \"global\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 810,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"1787f08ed7070ddd\",\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"431f917c2541ae48\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Routine\",\n        \"func\": \"from OpenScan import load_bool, load_str, load_int, load_float, motorrun, create_coordinates, take_photo, save, load_bool, camera\\nfrom time import sleep, strftime, time\\nfrom zipfile import ZipFile, ZIP_DEFLATED\\nfrom os import system\\nfrom os.path import isfile\\nfrom Arducam import Focuser\\n\\nif load_str(\\\"status_internal_cam\\\")==\\\"no camera found\\\" or load_str(\\\"status_internal_cam\\\")[:5]==\\\"Featu\\\":\\n    return\\n\\nsave('status_internal_cam','Routine-preparing')\\n\\nprojectname=load_str(\\\"routine_projectname\\\")\\nphotocount = load_int('routine_photocount') #vorher point_count\\nangle_max = load_int('rotor_anglemax')\\nangle_min = load_int('rotor_anglemin')\\nangle_start = load_int('rotor_anglestart')\\ncam = load_str('camera')\\nSTmode = load_bool('cam_STmode')\\ntt_mode = load_bool('turntable_mode')\\ncam_delay_after = load_float('cam_delay_after')\\ncam_delay_before = load_float('cam_delay_before')\\n\\nif cam == 'imx519' and STmode == True:\\n    focuser = Focuser('/dev/v4l-subdev1')\\n    stacksize = load_int('cam_stacksize')\\n    focus1 = load_int('cam_focus1')\\n    focus2 = load_int('cam_focus2')\\n    if focus1 > focus2:\\n        focus2 = focus1\\n        focus1 = load_int('cam_focus2') \\n    focusstep = int((focus2-focus1)/(stacksize - 1))\\n\\ncounter = 0\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\n\\nif not 'projectcode' in msg:\\n    projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n    msg['projectcode'] = projectcode\\n    msg['counter'] = -1\\n    if isfile(zippath):\\n        system('rm ' + zippath)\\n    sleep(1)\\n\\nprojectcode = msg['projectcode']\\nmsg['counter'] += 1\\n\\nif tt_mode == False:\\n    coordinates = create_coordinates(angle_min,angle_max,photocount)\\nelse:\\n    angle_start = 0\\n    coordinates = []\\n    for i in range (photocount):\\n        coordinates.append([0,360/photocount*(i+1)])\\n\\nposition_last = (angle_start , 0)\\n\\nzip = ZipFile(zippath, \\\"a\\\",ZIP_DEFLATED, allowZip64=True)\\n\\nstarttime = time()\\n\\nfor position in coordinates:\\n    counter += 1\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n\\n    while load_str('status_internal_cam') == 'Routine-paused':\\n        sleep(0.2)\\n\\n    filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \\\".jpg\\\"\\n\\n    rotor_angle = position_last[0] - position[0]\\n    if abs(rotor_angle) > 180:\\n        rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\\n\\n    tt_angle = position_last[1] - position[1]\\n    if tt_angle > 180:\\n        tt_angle -= 360\\n    elif tt_angle < -180:\\n        tt_angle += 360\\n    #    tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\\n            \\n    motorrun('rotor', rotor_angle)\\n    motorrun('tt', tt_angle)\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n    while load_str('status_internal_cam') == 'Routine-paused':\\n        sleep(0.2)\\n\\n    msg['cropx'] = load_int('cam_cropx')\\n    msg['cropy'] = load_int('cam_cropy')\\n    msg['rotation'] = load_int('cam_rotation')\\n    msg['filepath_in'] = 'tmp/tmp.jpg'\\n    msg['filepath_out'] = 'tmp/tmp.jpg'\\n    msg['filepath'] = 'tmp/tmp.jpg'\\n\\n    if counter < 6:\\n        ETA = ''\\n    sleep(cam_delay_before)\\n    if STmode == True:\\n        counter2 = 0\\n        for focus in range (stacksize):\\n            if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n                break\\n            while load_str('status_internal_cam') == 'Routine-paused':\\n                sleep(0.2)\\n            counter2 += 1\\n            save('status_internal_cam','Routine-' + str(counter) + '/' + str(photocount) + ' F' + str(counter2) + ETA)\\n            focuser.write(focus1 + focus * focusstep)\\n            take_photo('tmp/tmp.jpg')\\n            camera('/crop',msg)\\n            zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + '-' + str(focus) + \\\".jpg\\\")\\n            system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\\n    elif cam != 'external':\\n        save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\\n\\n        if cam == 'gphoto':\\n            camera('/gphoto_capture', msg)\\n        if cam in ('usb_webcam','imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\\n            take_photo('tmp/tmp.jpg')\\n        camera('/crop',msg)\\n        \\n        zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + \\\".jpg\\\")\\n        system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\\n    elif cam == 'external':\\n        camera('external_capture')\\n        save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\\n\\n    ETA = '-ETA:'+str(int((photocount/counter - 1)*(time() - starttime)))+'/'+str(int(photocount/counter*(time() - starttime)))+'s'\\n    sleep(cam_delay_after)\\n\\n    position_last = position\\n\\nzip.close()\\n\\nsave('status_internal_cam','Routine-done')\\n\\nmotorrun('rotor',position_last[0] - angle_start)\\nmotorrun('tt',position_last[1])\\n\\nsave('status_internal_cam','--READY--')\\n\\nif load_bool('routine_secondpass')==True:\\n    msg['topic'] = 'Scan done'\\n    msg['payload'] = 'Do you want to run another pass or finish this project?'\\n    msg['enabled'] = False\\n    return msg,None\\n\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 300,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"db7eea74d3bf892b\"\n            ],\n            [\n                \"0b8661103366f834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1787f08ed7070ddd\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nsave('status_internal_cam', 'Routine-stopping')\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b13dfd9f8d3711\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 395,\n        \"y\": 840,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9654deebb668e012\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 290,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8367cfa0bf5bc5df\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine\",\n        \"links\": [\n            \"210ef5246d1a8790\",\n            \"84608db962fd9932\",\n            \"8689e938.dd9e38\",\n            \"f20f2dbc.0f123\",\n            \"e9b13dfd9f8d3711\",\n            \"96bdb9417e38810f\",\n            \"fb13752beddee9f2\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 55,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"7dd287f40385922f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fb13752beddee9f2\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 895,\n        \"y\": 920,\n        \"wires\": []\n    },\n    {\n        \"id\": \"95439678bb2df2a2\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.flag = global.get('flag')\\nif (global.get('flag_pw')== true){\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"04cc2467807d2d6b\",\n                \"14f9617b5b301318\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"948a3ae4444685f2\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 610,\n        \"y\": 1260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"04cc2467807d2d6b\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 390,\n        \"y\": 1260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"12f1399b240830bf\",\n        \"type\": \"exec\",\n        \"z\": \"1613373abaf77a2c\",\n        \"command\": \" v4l2-ctl --list-formats-ext\",\n        \"addpay\": \"\",\n        \"append\": \"\",\n        \"useSpawn\": \"true\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"check cam\",\n        \"x\": 190,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"6222f781629c72e7\"\n            ],\n            [\n                \"6222f781629c72e7\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"6222f781629c72e7\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = '--READY--'\\n\\nif (msg.payload.includes('Cannot open device')){\\n    content = 'no camera found'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 350,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"e89c16809f8a5f1c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e978bf8c53d1f15a\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Settings internal cam\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ccb7da246de908d1\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview internal cam\",\n        \"info\": \"\",\n        \"x\": 110,\n        \"y\": 1160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e9566588c5e40637\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"4s/0.5\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.5\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"4\",\n        \"topic\": \"Repeat\",\n        \"payload\": \"0.2\",\n        \"payloadType\": \"str\",\n        \"x\": 80,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"14f9617b5b301318\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Take Preview Shot\",\n        \"func\": \"from time import time, sleep\\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\\nsleep(0.5)\\n\\n\\nstatus = load_str('status_internal_cam')\\ncam=load_str('camera')\\n\\n\\nif msg['flag'] == False and not 'Routine' in status:\\n    return msg\\n\\nif cam == 'external':\\n    return\\n\\nmsg['payload']=\\\"/tmp/preview.jpg?ts=\\\"+str(int(time()*10))\\n\\nif cam == 'gphoto' and status == 'no camera found':\\n    if camera('/gphoto_init') == 200:\\n        save('status_internal_cam','--READY--')\\n\\nif status!=\\\"--READY--\\\":\\n    return msg\\n\\nmsg['cropx'] = load_int('cam_cropx')\\nmsg['cropy'] = load_int('cam_cropy')\\nmsg['rotation'] = load_int('cam_rotation')\\nmsg['filepath_in'] = 'tmp/tmp.jpg'\\nmsg['filepath_out'] = 'tmp/preview.jpg'\\nmsg['filepath'] = 'tmp/tmp.jpg'\\nmsg['preview'] = True\\n\\nif cam == 'gphoto':\\n    if camera('/gphoto_test', msg) != 200:\\n        save('status_internal_cam','no camera found')\\n        return msg\\n    camera('/gphoto_preview', msg)\\n\\nif cam in ('usb_webcam', 'imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\\n    take_photo('tmp/tmp.jpg')\\n\\ncamera('/crop',msg)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 410,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"948a3ae4444685f2\",\n                \"991b587d406d0d91\",\n                \"8f5d87ce24c40b11\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"991b587d406d0d91\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"ce9cc9d915dc6eb6\",\n        \"name\": \"preview_internal\",\n        \"order\": 1,\n        \"width\": 12,\n        \"height\": 12,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 606px;max-height: 606px\\\"/>\\n\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 620,\n        \"y\": 1220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1118d0965ff7c40b\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"projectname\",\n        \"label\": \"Projectname\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"82c8ad50ecfbc755\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ringlight\",\n        \"label\": \"Ringlight\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 660,\n        \"y\": 700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"33d94a04b96a2de0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"global.set('flag', false)\\n\\nvar file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\n\\n\\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\\n    return\\n}\\n\\nmsg.enabled = true\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c1c044f3c2139f68\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a368472a72fbc48\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview arducam with focus\",\n        \"info\": \"\",\n        \"x\": 140,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8f5d87ce24c40b11\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"preview_arducam\",\n        \"order\": 2,\n        \"width\": 10,\n        \"height\": 12,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 502px;max-height: 606px\\\"/>\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 630,\n        \"y\": 1300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"282efe64332193c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import load_str\\n\\nif load_str('camera') != 'imx519':\\n    return\\n\\nfrom Arducam import Focuser\\n\\n\\nif msg['focuser'] == True:\\n    focuser = Focuser('/dev/v4l-subdev1')\\n    focuser.write(msg['focus'])\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 1110,\n        \"y\": 1460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"64b16ef47ab6d859\",\n        \"type\": \"ui_switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"MF\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"false\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"true\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"f017f67a8d4a3750\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f017f67a8d4a3750\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"let fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\n\\nvar file = 'status_internal_cam'\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data != '--READY--'){\\n    return\\n}\\n\\nfile = 'cam_AFmode'\\ncontent = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n});\\n\\nglobal.set('AF',msg.payload)\\nmsg.enabled = false\\nif (msg.payload == false){\\n    msg.enabled = true\\n}\\nif (msg.payload == true){\\n    file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    \\n    file = 'cam_stacksize'\\n    content = String(2)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('focus1', 0)\\n    global.set('focus2', 0)\\n\\n}\\n\\n\\nmsg.focus = global.get('focus')\\nmsg.payload = 'down'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"5c39bd09.702d84\",\n                \"74521cf72050b515\",\n                \"b70e8c24ee011258\",\n                \"a2ff9dfd858821bc\",\n                \"ef62086d10d830fd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65145c939b6647e2\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 55,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"64b16ef47ab6d859\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5ea18678.975138\",\n        \"type\": \"trigger\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"20ms\",\n        \"op1\": \"\",\n        \"op2\": \"0\",\n        \"op1type\": \"pay\",\n        \"op2type\": \"str\",\n        \"duration\": \"-20\",\n        \"extend\": false,\n        \"overrideDelay\": false,\n        \"units\": \"ms\",\n        \"reset\": \"\",\n        \"bytopic\": \"all\",\n        \"topic\": \"topic\",\n        \"outputs\": 1,\n        \"x\": 730,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"fd93843e238cc9ce\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c39bd09.702d84\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F+\",\n        \"order\": 8,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: -1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-plus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dcfb5cce.0431a\",\n        \"type\": \"switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"property\": \"payload\",\n        \"propertyType\": \"msg\",\n        \"rules\": [\n            {\n                \"t\": \"eq\",\n                \"v\": \"1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"-1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"up\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"checkall\": \"true\",\n        \"repair\": false,\n        \"outputs\": 3,\n        \"x\": 550,\n        \"y\": 1420,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"8cdd0a6b.40bcd8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8cdd0a6b.40bcd8\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"reset\",\n                \"pt\": \"msg\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 560,\n        \"y\": 1480,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"e9b3837b1ffb0360\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"74521cf72050b515\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F-\",\n        \"order\": 9,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: 1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-minus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7219f62c9fdc6753\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"{{msg.payload}}\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 1130,\n        \"y\": 1420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b70e8c24ee011258\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"global\",\n        \"func\": \"if (msg.payload == 'down'){\\n    msg.enabled = false\\n    msg.payload = ' '\\n    msg.focuser = global.get('focuser')\\n    return msg\\n}\\n\\n\\nmsg.enabled = true\\n\\nsign = msg.payload\\nfocus = global.get('focus')\\nif (focus > 3000){\\n    focusstep = 5\\n}\\nelse if (focus <=3000 && focus > 2000){\\n    focusstep = 3\\n}\\nelse{\\n    focusstep = 2\\n}\\n\\n\\nfocus = focus + sign * focusstep\\n\\nsign = msg.payload\\nif (focus > 4000){\\n    distance = 6\\n    focus = 4000\\n}\\nelse if (focus > 1200 && focus <= 4000){\\n    distance = 737086 * Math.pow(focus, -1.4096)\\n}\\nelse if (focus <= 1200){\\n    distance = 999\\n    if (focus <=0){\\n       focus = 0\\n    }\\n}\\n\\n\\nglobal.set('focus', focus)\\nmsg.focus = focus\\nmsg.distance = distance\\ndistance = distance * 10\\nmsg.focuser = global.get('focuser')\\nmsg.payload = String(distance.toFixed(1)) + 'mm'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 970,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"7219f62c9fdc6753\",\n                \"282efe64332193c8\",\n                \"704a9f89089d1f25\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4a41b1e7b221486\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser f\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b3837b1ffb0360\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser t\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fd93843e238cc9ce\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"10ms\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"20\",\n        \"timeoutUnits\": \"milliseconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 850,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"b70e8c24ee011258\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"25c4138bddb77b6b\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"set\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 570,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"95e1d239988b29e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"95e1d239988b29e0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"focus = global.get('focus')\\nfocus1 = global.get('focus1')\\nfocus2 = global.get('focus2')\\nlet fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\n    \\nif (msg.payload == false){\\n    return msg\\n}\\n\\nif (focus1 != 0 && focus2 != 0){\\n    global.set('focus1', 0)\\n    global.set('focus2', 0)\\n        file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    msg.distance1 = ' '\\n    msg.distance2 = ' '\\n    msg.enabled = false\\n    return msg\\n}\\n\\nif (focus > 4000){\\n    distance = 6\\n    focus = 4000\\n}\\nelse if (focus > 1200 && focus <= 4000){\\n    distance = 737086 * Math.pow(focus, -1.4096)\\n}\\nelse if (focus <= 1200){\\n    distance = 999.9\\n    if (focus <=0){\\n       focus = 0\\n    }\\n}\\ndistance = distance * 10\\n\\nif (focus1 == 0){\\n    global.set('focus1', focus)\\n    file = 'cam_focus1'\\n    content = String(focus)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('distance1', distance)\\n    msg.distance1 = distance.toFixed(1)\\n    msg.distance2 = 'tbd'\\n    msg.enabled = false\\n    return msg\\n}\\nif (focus1 != 0 && focus2 ==0 && focus!= focus1){\\n    global.set('focus2', focus)\\n    file = 'cam_focus2'\\n    content = String(focus)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('distance2', distance)\\n    msg.distance1 = global.get('distance1').toFixed(1)\\n    msg.distance2 = distance.toFixed(1)\\n    msg.enabled = true\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 710,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"7889245e91ddea4b\",\n                \"210ef5246d1a8790\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7889245e91ddea4b\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"{{msg.distance1}}\",\n        \"format\": \"{{msg.distance2}}\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 830,\n        \"y\": 1600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a1d29e56599da0bd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focusnumber\",\n        \"links\": [\n            \"210ef5246d1a8790\",\n            \"2dd2503d7ab0214b\",\n            \"6b94bf2295b1b31d\"\n        ],\n        \"x\": 175,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"06504f47ee1744d7\",\n                \"5f8b90ef08a7d68c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"210ef5246d1a8790\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\",\n            \"8367cfa0bf5bc5df\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 835,\n        \"y\": 1560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b6f37e23f2491639\",\n        \"type\": \"ui_switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Stack\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 6,\n        \"width\": 1,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 290,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"2d66216fee29250c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a2ff9dfd858821bc\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.payload = false\\nif (msg.enabled == false){\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"25c4138bddb77b6b\",\n                \"7889245e91ddea4b\",\n                \"4cfada2de1c5bb74\",\n                \"95e1d239988b29e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2d66216fee29250c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"file = 'cam_STmode'\\nlet fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\ncontent = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n});\\n\\nmsg.enabled = true\\nglobal.set('ST',msg.payload)\\nif (msg.payload == false){\\n    global.set('focus1',0)\\n    global.set('focus2',0)\\n    file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n        \\n    \\n    msg.enabled = false\\n}\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 430,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"25c4138bddb77b6b\",\n                \"7889245e91ddea4b\",\n                \"2dd2503d7ab0214b\",\n                \"4cfada2de1c5bb74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ef62086d10d830fd\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.payload = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 150,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"b6f37e23f2491639\",\n                \"523019d0a2c698f5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"06504f47ee1744d7\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 12,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Stacksize:\",\n        \"format\": \"{{msg.stacksize}}\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 710,\n        \"y\": 1760,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2dd2503d7ab0214b\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\"\n        ],\n        \"x\": 535,\n        \"y\": 1620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"21306d6402225553\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.stacksize = msg.payload\\nmsg.enabled = true\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"06504f47ee1744d7\",\n                \"ca184d58f7deb4b1\",\n                \"84608db962fd9932\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e2f8fdd47bdd1b66\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stacksize\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 13,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"2\",\n        \"max\": \"20\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"21306d6402225553\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"523019d0a2c698f5\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 5,\n        \"width\": 1,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"St\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 290,\n        \"y\": 1560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dfbfe28bac5c4221\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 3,\n        \"width\": 1,\n        \"height\": 1,\n        \"name\": \"MF\",\n        \"label\": \"MF\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ca184d58f7deb4b1\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"save\",\n        \"func\": \"var file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.stacksize)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 690,\n        \"y\": 1720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"704a9f89089d1f25\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"save\",\n        \"func\": \"var file = 'cam_focus'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.focus)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1110,\n        \"y\": 1500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"5f8b90ef08a7d68c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"e2f8fdd47bdd1b66\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4cfada2de1c5bb74\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"if (msg.enabled == true){\\n    msg.enabled = false\\n}\\nelse{\\n    msg.enabled = true\\n}\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 570,\n        \"y\": 1660,\n        \"wires\": [\n            [\n                \"84608db962fd9932\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"84608db962fd9932\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 675,\n        \"y\": 1660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e89c16809f8a5f1c\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"gphoto\",\n        \"func\": \"\\nfrom OpenScan import camera, save, load_str\\n\\nif load_str('camera') == 'gphoto':\\n    if camera('/gphoto_init') == 200:\\n        if camera('/gphoto_test') == 200:\\n            save('status_internal_cam','--READY--')\\n            return msg\\nif load_str('camera') == 'external':\\n    save('status_internal_cam','--READY--')\",\n        \"outputs\": 1,\n        \"x\": 490,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"917a194be245384a\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65cef204b16f8741\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable shutter\",\n        \"links\": [\n            \"2d76e5617f13cd6c\",\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"84d6b96c8ebaac96\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2aea1727dbea76ce\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropx\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"9c6b48b7b4cc4e1a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f212b44aa487945\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropy\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"c470fd0b15356206\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d1e12f51f9af0b6\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start camchk\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 55,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"12f1399b240830bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8ebd1dcb5db156ed\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Current Status:\",\n        \"format\": \"<font color= {{msg.color}} > {{msg.payload}} </font>\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"94a7aec739f9266b\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\n\\nif (data === 'no camera found'){\\n    msg.color = 'red'\\n}\\n\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"8ebd1dcb5db156ed\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2415272f42ce468c\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start status\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 55,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"94a7aec739f9266b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1e14624058e74cd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 55,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\",\n                \"107a030938cbfea9\",\n                \"84d6b96c8ebaac96\",\n                \"9c6b48b7b4cc4e1a\",\n                \"c470fd0b15356206\",\n                \"9e30e33a1520fee0\",\n                \"79ecb889f7113405\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1daf9e3a5bd5ab48\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nmsg.enabled = true\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"fb13752beddee9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d15f717d5a11002\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 300,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"e9b13dfd9f8d3711\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d14bbbb446d45e39\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview\",\n        \"links\": [\n            \"f20da2fc4978b7bf\"\n        ],\n        \"x\": 135,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"db7eea74d3bf892b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"1613373abaf77a2c\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Finish\",\n        \"cancel\": \"2nd pass\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 510,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"0b8661103366f834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0b8661103366f834\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"continue\",\n        \"func\": \"from os import system\\nfrom os.path import isfile\\n\\n\\nif msg['payload'] == '2nd pass':\\n    msg['enabled'] = True\\n    return msg,None\\n\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectcode = msg['projectcode']\\n\\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\\n\\nmsg['path'] = basepath + 'scans/' + projectcode + '.zip'\\n\\nif isfile(zippath):\\n    system('rm ' + zippath)\\n\\nreturn None, msg\",\n        \"outputs\": 2,\n        \"x\": 660,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ],\n            [\n                \"1daf9e3a5bd5ab48\",\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"79ecb889f7113405\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"inactive\",\n        \"func\": \"from requests import get\\nfrom OpenScan import load_int\\n\\ntimeout = load_int('timeout_ringlight')\\n\\nmsg['cmd'] = 'get'\\n\\ntry:\\n    flask = 'http://127.0.0.1:1312/ping'\\n    r = get(flask, params=msg)\\n\\n    idle = float(r.text.split(\\\":\\\")[1].split('}')[0])\\n\\n    msg['payload'] = idle\\n\\n    if idle > timeout:\\n        return msg,msg\\nexcept:\\n    pass\\n\\nreturn None,msg\",\n        \"outputs\": 2,\n        \"x\": 200,\n        \"y\": 740,\n        \"wires\": [\n            [\n                \"9e30e33a1520fee0\"\n            ],\n            [\n                \"8d7e04531c34f349\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8d7e04531c34f349\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"30\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 200,\n        \"y\": 780,\n        \"wires\": [\n            [\n                \"79ecb889f7113405\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c433515042ba01b5\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"pause\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 22,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-pause\",\n        \"payload\": \" \",\n        \"payloadType\": \"str\",\n        \"topic\": \"Scan paused\",\n        \"topicType\": \"str\",\n        \"x\": 810,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"63db399d8ac2acb6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"63db399d8ac2acb6\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"pause\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nif status == 'Routine-paused':\\n    save('status_internal_cam', 'Routine-continue')\\nelse:\\n    save('status_internal_cam', 'Routine-paused')\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 1040,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"44c598049cd533fd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"crop\",\n        \"links\": [\n            \"f358de1e64b491bb\"\n        ],\n        \"x\": 595,\n        \"y\": 640,\n        \"wires\": [\n            [\n                \"fecf5cff888bb570\",\n                \"0ee4950bd21498bd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ea54fcc2.cfcc2\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"get dirs\",\n        \"func\": \"from glob import glob\\nimport os\\nfrom zipfile import ZipFile\\nfrom datetime import datetime\\nfrom PIL import Image\\n\\ndef set_stats(stat):\\n    try:\\n        with open(directory+set[:-4]+\\\"/\\\"+stat,\\\"r\\\") as file:\\n            stat=file.read()\\n    except:\\n        stat=\\\"\\\"\\n    return stat\\n\\ntable=[]\\ndirectory=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nfor d in glob(directory+\\\"*.zip\\\"):\\n    set=os.path.basename(d)\\n\\n    try:\\n        with ZipFile(d, 'r') as f:\\n            photos = len(f.namelist())\\n        \\n            if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\\n                image = f.open(f.namelist()[int(photos/2)])\\n                img = Image.open(image)\\n                width, height = img.size\\n                width_factor = width/300\\n                height_factor = height/295\\n                if height_factor>=width_factor and height_factor > 1:\\n                    new_size=(int(width/height_factor), int(height/height_factor))\\n                    img = img.resize(new_size)\\n                elif height_factor<width_factor and width_factor > 1:\\n                    new_size=(int(width/width_factor),int(height/width_factor))\\n                    img = img.resize(new_size)\\n                img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\\n        list=[]\\n        for fi in f.filelist:\\n            list.append(f.getinfo(fi.filename).date_time)\\n        \\n        duration = str(datetime(*max(list)) - datetime(*min(list)))\\n        \\n        size = float(int(float(os.path.getsize(d))/100000))/10\\n        size_full= os.path.getsize(d)\\n        status=set_stats(\\\"status\\\")\\n        expiration=set_stats(\\\"expiration\\\")\\n        download=set_stats(\\\"download\\\")\\n    \\n        if len(download)!=0:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Download\\\":\\\"<a href=\\\"+download+\\\">RESULT</a>\\\",\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n                })\\n        else:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n\\n                })\\n    except:\\n        pass\\n\\nmsg['payload']=table\\nmsg['topic']=\\\"\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 480,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"b9a3a0f9.bcbea\",\n                \"f3662f8c7d3d7a2d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2f4c0f98.dee2\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"filelist\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"a4f09e25.02569\",\n            \"ed35109311335099\",\n            \"fb13752beddee9f2\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 355,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b9a3a0f9.bcbea\",\n        \"type\": \"ui_table\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"b5fdd57b.15eda8\",\n        \"name\": \"\",\n        \"order\": 1,\n        \"width\": 13,\n        \"height\": 7,\n        \"columns\": [\n            {\n                \"field\": \"Date\",\n                \"title\": \"Date\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Name\",\n                \"title\": \"Name\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Photos\",\n                \"title\": \"Photos\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Duration\",\n                \"title\": \"ΔT\",\n                \"width\": \"60\",\n                \"align\": \"left\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Size\",\n                \"title\": \"Size\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Status\",\n                \"title\": \"Status\",\n                \"width\": \"140\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            }\n        ],\n        \"outputs\": 1,\n        \"cts\": true,\n        \"x\": 610,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"50710948.71c308\",\n                \"4082b136.dae18\",\n                \"834046a4.647938\",\n                \"0c387c0291d6c131\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"952ce286.4ffd4\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Status\",\n        \"label\": \"Status\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 250,\n        \"y\": 60,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d4383424.7807c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"upload\",\n        \"func\": \"import os\\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\\nfrom subprocess import getoutput\\n\\nbasedir = '/home/pi/OpenScan/'\\n\\nif load_str(\\\"feedback_terms\\\")==\\\"False\\\":\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic'] = 'OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nmsg = msg['payload']\\n\\ndef upload(filelist, ulinks):\\n    pid = getoutput('pidof curl')\\n    if pid != \\\"\\\":\\n        os.system('kill ' + pid)\\n\\n    i = 0\\n    for file in filelist:\\n        link = ulinks[i]\\n        save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\\n        cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\\\"' + file + '\\\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\\n        i = i+1\\n        os.system(cmd)\\n\\n########\\nif not os.path.isfile(basedir + 'settings/token'):\\n    msg['flag'] = True\\n    save('status_cloud', 'please enter token first')\\n    return msg\\nwith open(basedir + 'settings/token', 'r') as file:\\n    token = file.read().strip('\\\\n')\\n\\n########\\nr = OpenScanCloud('getTokenInfo', {'token':token})\\n\\nif r.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    save('status_cloud', 'invalid/missing token')\\n    return None,msg\\nelif r.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\n########\\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\\n    msg['flag'] = True\\n    save('status_cloud', 'limit(s) exceeded')\\n    return msg\\n\\n########\\ntemp =  OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\\nif temp.status_code not in (200,401):\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nif temp.status_code != 401:\\n    temp = temp.json()\\n    if 'status' in temp:\\n        if temp['status'] != 'created':\\n            save('status_cloud','already exists')\\n            with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\\n                file.write(temp['status'])\\n            return msg\\n#####\\n\\nmsg2={}\\nmsg2['token'] = token\\nmsg2['parts'] = 1\\nmsg['partslist']=[]\\n\\n#######\\nsize_to_split = load_int('osc_splitsize')\\n\\nif msg['Size_full'] > size_to_split:\\n    tempdir = basedir + 'tmp/split/'\\n    if os.path.isdir(tempdir):\\n        os.system('rm -r ' + tempdir)\\n    os.mkdir(tempdir)\\n    save('status_cloud', 'zipping files, please wait ...')\\n    cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\\n    os.system(cmd)\\n    save('status_cloud', 'zip done')\\n    list = os.listdir(tempdir)\\n    for l in list:\\n        msg['partslist'].append(tempdir + l)\\n        msg['partslist'].sort()\\n    msg2['parts']=len(msg['partslist'])\\nelse:\\n    msg['partslist'] = [basedir + 'scans/' +msg['Set']]\\n\\n#######\\nmsg2['photos'] = msg['Photos']\\nmsg2['filesize'] = msg['Size_full']\\nmsg2['project'] = msg['Set']\\n\\nr = OpenScanCloud('createProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\\n    os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\\n    file.write('prepared')\\n\\nsave('status_cloud', 'uploading')\\nupload(msg['partslist'], msg1['ulink'])\\n\\nr = OpenScanCloud('startProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Upload failed'\\n    msg['payload'] = 'please try again'\\n    save('status_cloud', 'upload failed')\\n    return None,msg\\n\\nsave('status_cloud', 'uploaded')\\n\\nsave('status_cloud', 'project started')\\n\\ntry:\\n    os.system('rm -r ' + tempdir)\\nexcept:\\n    pass\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 530,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"9a132ab1.b21658\"\n            ],\n            [\n                \"3d16b3789632784d\",\n                \"9a132ab1.b21658\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50710948.71c308\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"set\",\n                \"pt\": \"global\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 750,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ada1b6f7cccc9344\",\n                \"85839a17fb7b58b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"834046a4.647938\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Set\",\n        \"label\": \"Set:\",\n        \"format\": \"{{msg.payload.Name}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9a132ab1.b21658\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 780,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"8689e938.dd9e38\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3c67e97b.9d19a6\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable\",\n        \"func\": \"if (global.get('flag') === false){\\n    msg.enabled = false\\n    msg.color=\\\"white\\\"\\n}\\nelse{\\n    msg.enabled = true\\n    msg.color=\\\"red\\\"\\n    \\n}\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 130,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"7a93d1e18254685c\",\n                \"e434ef42bd6b92e8\",\n                \"d5d840183025d91b\",\n                \"ab9e90ab5a53a0dd\",\n                \"478994f671a3907d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bfc01f26.c32cf\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 420,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"f20f2dbc.0f123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b33d604c.5f1a6\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable cloud\",\n        \"links\": [\n            \"4082b136.dae18\",\n            \"8689e938.dd9e38\",\n            \"bd75f33b8a57c522\",\n            \"e9b13dfd9f8d3711\",\n            \"f20f2dbc.0f123\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 35,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"3c67e97b.9d19a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f6bd1a04.470838\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"payload\",\n                \"pt\": \"msg\",\n                \"to\": \"set\",\n                \"tot\": \"global\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 410,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"d4383424.7807c8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4082b136.dae18\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"b33d604c.5f1a6\",\n            \"87574a42938afec4\"\n        ],\n        \"x\": 715,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f20f2dbc.0f123\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 515,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8689e938.dd9e38\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 875,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15de0ebb.616d61\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a7d89487.ee8858\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a7d89487.ee8858\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\ntry:\\n    os.remove(dir+msg['Set'])\\n    shutil.rmtree(dir+msg['Set'][:-4])\\nexcept:\\n    pass\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4f09e25.02569\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"c20357dd.374108\",\n            \"e9aab326.a6896\",\n            \"edd22cc7.befe1\",\n            \"19b81967.49db87\",\n            \"8ee1b3bb.7b0b3\",\n            \"d5246b3cc796afc6\"\n        ],\n        \"x\": 775,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7a93d1e18254685c\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"92c98e6ce7cd25f9\"\n        ],\n        \"x\": 235,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d99c601c9881680\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"refresh\",\n        \"func\": \"from time import sleep\\nimport os\\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\n\\nif load_bool(\\\"terms\\\")==False:\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic']='OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nsave('status_cloud','refreshing')\\ntoken = load_str('token')\\n\\ntest = OpenScanCloud('getTokenInfo',{'token':token})\\nif test.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    return None,msg\\nelif test.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nstats = test.json()\\nfor i in stats:\\n    save('osc_'+i, stats[i])\\n    pass\\n\\nmsg={}\\nprojects = []\\nfor i in os.listdir(basepath):\\n    if i == 'preview':\\n        continue\\n    if os.path.isdir(basepath + i):\\n        if os.path.isfile(basepath + i + '/status'):\\n            with open(basepath + i + '/status', 'r') as file:\\n                status = file.read().strip('\\\\n')\\n                if status in ['expired', 'processing done', 'processing failed']:\\n                    continue\\n        projects.append(i)\\n\\nfor p in projects:\\n    r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\\n    if r.status_code == 200:\\n        answer = r.json()\\n        if answer == {}:\\n            os.system('rm -r ' + basepath + p)\\n        else:\\n            with open(basepath + p + '/status', 'w+') as file:\\n                file.write(answer['status'])\\n            with open(basepath + p + '/download', 'w+') as file:\\n                file.write(answer['dlink'])\\n\\nmsg['list'] = projects\\nsleep(0.5)\\nsave('status_cloud','ready')\\nreturn msg, None\\n\",\n        \"outputs\": 2,\n        \"x\": 320,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\",\n                \"b42e061fb1f1f3d7\"\n            ],\n            [\n                \"6434e713f088012b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"372e95797a3f2f3b\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"limit :)\",\n        \"func\": \"from time import sleep\\n\\nmsg2={}\\nmsg2['enabled'] = True\\n\\nmsg['enabled'] = False\\nnode.send(msg)\\n\\nwait = 15\\n\\nfor i in range (wait):\\n    msg['text'] = ' ('+ str(wait - i)+')'\\n    node.send(msg)\\n\\nmsg['enabled'] = True\\nmsg['text']=\\\"\\\"\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 90,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"573edbfdb7500ddc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573edbfdb7500ddc\",\n        \"type\": \"delay\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dacb1f078b624e10\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"c8d65cc7c2ff7c36\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92c98e6ce7cd25f9\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"7a93d1e18254685c\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 35,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3d16b3789632784d\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 770,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6434e713f088012b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 470,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8d65cc7c2ff7c36\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\nfor i in os.listdir(dir):\\n    if os.path.isdir(dir + i):\\n        shutil.rmtree(dir + i)\\n    else:\\n        os.remove(dir + i)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4e9a4bd79b4221f\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dacb1f078b624e10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2806bf08ea21216d\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.Set=global.get('set')['Set']\\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"15de0ebb.616d61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"61990987acd0f263\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 45,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"51579603bce21e98\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e8e488a6dd5d0b33\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"Download\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href={{msg.download}}>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 880,\n        \"y\": 260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0c387c0291d6c131\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.download = '/scans/' + String(msg.payload.Set)\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"e8e488a6dd5d0b33\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e5f38b4a07a5e278\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 655,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"834046a4.647938\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e434ef42bd6b92e8\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"upload2\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">upload</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f6bd1a04.470838\",\n                \"bfc01f26.c32cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c46e10b9c201913e\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"refresh\",\n        \"order\": 1,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">refresh{{msg.text}}</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 160,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"372e95797a3f2f3b\",\n                \"4d99c601c9881680\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d5d840183025d91b\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del set\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;color:red; width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"2806bf08ea21216d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab9e90ab5a53a0dd\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del \",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete all</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"f4e9a4bd79b4221f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"478994f671a3907d\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"combine\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">combine</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"51bfd0fb7b1d292e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"189c1eed09624a7b\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"combine = global.get('combine')\\ncombine_set = global.get('set').Set\\n\\nif (combine === true && global.get('combine_set') !== combine_set){\\n    msg.set1 = global.get('combine_set')\\n    msg.set2 = combine_set\\n    global.set('combine', false)\\n    msg.topic = 'Combine the following two sets:'\\n    msg.payload = msg.set1 + '<br>' + msg.set2 + '<br>FILES WILL BE MERGED INTO ON FILE!'\\n    return msg\\n}\\nglobal.set('combine_set' , combine_set)\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"1493398979a63775\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51bfd0fb7b1d292e\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"global.set('combine', true)\\ncombine_set = global.get('set').Set\\nmsg.topic = 'Merge two sets into one (can not be undone)!'\\nmsg.payload = combine_set\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 420,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"da325be8e74179be\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"from os.path import getsize\\nfrom shutil import copy\\nfrom os import rename, remove\\nimport zipfile as z\\nfrom OpenScan import save\\n\\nfrom time import sleep\\n\\nif msg['payload'] != 'OK':\\n    return\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\ntmp1 = basepath + msg['set1']\\ntmp2 = basepath + msg['set2']\\n\\nif getsize(tmp1) > getsize(tmp2):\\n    set1 = tmp1\\n    set2 = tmp2\\nelse:\\n    set1 = tmp2\\n    set2 = tmp1\\n\\nzips = [set1, set2]\\n\\nwith z.ZipFile(set1, 'a') as z1:\\n    z2 = z.ZipFile(set2, 'r')\\n    i = 0\\n    for n in z2.namelist():\\n        i += 1\\n        n2 = n\\n        save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\\n        while 'X'+n in z1.namelist():\\n            n = 'X' + n\\n        z1.writestr('X'+n, z2.open(n2).read())\\nsave('status_cloud','ready')\\n\\nos.rename(set1, set1[:-4] + 'X.zip')\\nos.remove(set2)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 560,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"ed35109311335099\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ed35109311335099\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"2f4c0f98.dee2\"\n        ],\n        \"x\": 655,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1493398979a63775\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Combine\",\n        \"x\": 420,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"da325be8e74179be\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ada1b6f7cccc9344\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"6dd356510c446cf4\"\n        ],\n        \"x\": 835,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6dd356510c446cf4\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"links\": [\n            \"ada1b6f7cccc9344\"\n        ],\n        \"x\": 175,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"189c1eed09624a7b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b42e061fb1f1f3d7\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"397ab7f44b893c89\"\n        ],\n        \"x\": 435,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b99505440832439f\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"diskspace\",\n        \"func\": \"from subprocess import getoutput\\nfrom OpenScan import load_int\\n\\ndiskspace_threshold = load_int('diskspace_threshold')\\n\\ndiskspace = getoutput('df -h / | awk \\\"{print $5}\\\"').split('\\\\n')[1]\\n\\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\\n\\n\\nif available < diskspace_threshold:\\n    msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \\n    msg['payload'] = 'Please delete some/all locally stored files.'\\n    msg['color'] = 'red'\\n    return msg\\n\",\n        \"outputs\": 1,\n        \"x\": 800,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"92047434f8e9f927\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92047434f8e9f927\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 950,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f3662f8c7d3d7a2d\",\n        \"type\": \"delay\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"minute\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 650,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"b99505440832439f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51579603bce21e98\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"read\",\n        \"func\": \"from OpenScan import load_str\\nfrom os import listdir, path\\n\\nstatus = load_str('status_cloud')\\n\\nif status[0:9] == 'uploading':\\n    progress = load_str('status_uploadprogress')[-6:]\\n    if progress[-1:] == '%':\\n        status = status + ' (' + progress + ')'\\n\\nif status[0:7] == 'zipping':\\n    path1 = '/home/pi/OpenScan/tmp/split/'\\n    files = listdir(path1)\\n    size1 = 0\\n    for file in files:\\n        size1 += path.getsize(path1+file)\\n    size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\\n    \\n    status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\\n\\nmsg['status'] = status\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 130,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"952ce286.4ffd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a5baae623355f9d\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"preview\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 6,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 301px;max-height: 301px\\\"/>\\n\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 1020,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"85839a17fb7b58b9\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"preview\",\n        \"func\": \"from time import time\\nimport os\\n\\npath = '/home/pi/OpenScan/scans/preview/'\\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\\n\\nmsg['payload']=\\\"/scans/preview/\\\" + image +\\\"?ts=\\\"+str(int(time()*10))\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 880,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"9a5baae623355f9d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"45058bfcf047e8cc\",\n        \"type\": \"inject\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 100,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"40dee936a9abac0d\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"SSH\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dc354c54078ca607\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4fd9bb53fdb51a25\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Samba\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"test2\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"b0aa8ffae5a3578a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dc354c54078ca607\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ssh\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('ssh'):\\n    save('ssh', state)\\n\\nif state == True:\\n    os.system('/etc/init.d/ssh start')\\nelse:\\n    os.system('/etc/init.d/ssh stop')\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"52858b4eceacc902\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Terms Of Use\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 120,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"f99ec8781a33ec7d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"595153429adef33b\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Network Settings</h1> <p><strong>Hostname</strong></p> <p style=\\\"margin-left: 20px;\\\">The device can be accessed through any browser in the same network. Therefore, you can either enter the device&apos;s IP address or the given hostname. The standard name is &apos;openscan&apos; but it is highly recommended to change the name, when using multiple devices (e.g. &apos;openscan1&apos;, &apos;openscan2&apos; ...)</p> <p><strong>Select Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: <a href=\\\"%5C%22https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>ISO 3166 Country Code on Wikipedia</a></p> <p><strong>Search Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">You can manually refresh the list of available networks by pressing this button.</p> <p><strong>Reset Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see <a href=\\\"%5C%22https://www.glennklockwood.com/sysadmin-howtos/rpi-headless-boot.html%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>glennklockwood.com</a> for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 590,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dc39bd847d16ded\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Agree\",\n        \"cancel\": \"Disagree\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 410,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"5f849178998d9082\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"02858034e17b827f\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"General\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>General Settings</h1> <p><strong>Terms Of Use</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.</p> <p><strong>SSH</strong></p> <p style=\\\"margin-left: 20px;\\\">SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.</p> <p style=\\\"margin-left: 20px;\\\">If you want to use it, the default user is <strong>pi</strong>, password: <strong>raspberry. Please change the password&nbsp;</strong><strong>immediately.</strong>&nbsp;</p> <p><strong>Samba</strong></p> <p style=\\\"margin-left: 20px;\\\">Samba s a network local file sharing server, which allows accessing the Raspberry Pi&apos;s file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">You can access the Raspberry Pis file system by inserting the following line into your Windows explorer:&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">\\\\\\\\OpenScan/PiShare/OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">username: pi, password: raspberry</p> <p style=\\\"margin-left: 20px;\\\"><strong>Please deactivate the local file sharing if you do not intend to use it</strong></p> <p><strong>Advanced Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">Enable a ton of additional settings, which should be changed only if you know what you are doing ;)</p> <p><strong>Model</strong></p> <p style=\\\"margin-left: 20px;\\\">Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.</p> <p><strong>Camera</strong></p> <p style=\\\"margin-left: 20px;\\\">A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.</p> <p style=\\\"margin-left: 20px;\\\"><u>DSLR (gphoto)</u> - connect a wide range of DSLR cameras to the device through USB. See <a href=\\\"http://www.gphoto.org/proj/libgphoto2/support.php\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">GPhoto</a> for a full list of supported devices.</p> <p style=\\\"margin-left: 20px;\\\"><u>External camera</u> - triggering any camera through an isolated GPIO signal on the front side of the pi shield.</p> <p><strong>Shutdown/Reboot</strong></p> <p style=\\\"margin-left: 20px;\\\">Always use the shutdown button before you power off your Raspberry Pi.</p> <p><strong>Restore Default Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">In case you want to restore the default settings</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 740,\n        \"y\": 240,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"675d4933a44ae6b5\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Pinout\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1><span style=\\\"color: rgb(226, 80, 65);\\\">Pinout</span></h1> <p><span style=\\\"color: rgb(226, 80, 65);\\\"><strong><span style=\\\"font-size: 19px;\\\">ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!</span></strong></span></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b0aa8ffae5a3578a\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"smb\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('smb'):\\n    save('smb', state)\\nif state == True:\\n    os.system('/etc/init.d/smbd start')\\nelse:\\n    os.system('/etc/init.d/smbd stop')\\n\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 380,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cc3cb10f2ea3f8b8\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"blink Light1\",\n        \"func\": \"import RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nfrom OpenScan import ringlight\\nfrom time import sleep\\n\\ndelay = 0.1\\nringlight(2,False)\\n\\nfor i in range (5):\\n    ringlight(1,True)\\n    sleep(delay)\\n    ringlight(1,False)\\n    sleep(delay)\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d114f4d4d7f31981\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"func\": \"import os\\nfrom time import sleep\\nsleep(1.5)\\nimport RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nos.system('reboot -h')\\n\",\n        \"outputs\": 1,\n        \"x\": 270,\n        \"y\": 720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"79181ad3b56d5c62\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Model\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d81bd138733c410\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Camera\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 840,\n        \"y\": 420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"80b579a4220e5c23\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 8,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Please Select\",\n                \"value\": \"None\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Mini\",\n                \"value\": \"OSMini\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Classic\",\n                \"value\": \"OSClassic\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"896242c5a7e50fa7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a2c1dba3e67be015\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 10,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Pi Cam v1 - 5mp\",\n                \"value\": \"ov5647\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam v2 - 8mp\",\n                \"value\": \"imx219\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam HQ - 12.3mp\",\n                \"value\": \"imx477\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Arducam IMX519 - 16mp\",\n                \"value\": \"imx519\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 a\",\n                \"value\": \"imx290a\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 b\",\n                \"value\": \"imx290b\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX378\",\n                \"value\": \"imx378\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OV9281\",\n                \"value\": \"ov9281\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"DSLR (gphoto)\",\n                \"value\": \"gphoto\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"USB Webcam\",\n                \"value\": \"usb_webcam\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"External Camera\",\n                \"value\": \"external\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"4058a31e942e8f95\",\n                \"6d68cccec646e0a0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9cf5d56263caada7\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Motor\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Motor Settings</h1> <p><strong>Turntable Mode</strong></p> <p style=\\\"margin-left: 20px;\\\">Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.</p> <p><strong>Rotor - Start Angle, Min and Max Angle</strong></p> <p style=\\\"margin-left: 20px;\\\">Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0&deg; corresponds to the horizontal (natural) orientation.</p> <p style=\\\"margin-left: 20px;\\\">After that, the device will equally space the image positions between angle min and angle max.</p> <p><strong>Rotor/Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>Steps per rotation</u> - &nbsp;defines the number of steps it takes to move the axis 360&deg;. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)</p> <p style=\\\"margin-left: 20px;\\\"><u>Delay</u> - time in microseconds between each step of the motor. Lower this value if the movement is too fast</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration</u> - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration ramp</u> - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.</p> <p style=\\\"margin-left: 20px;\\\"><u>Manual Angle</u> - Defines the degree value for the manual movement through the arrow buttons in the scan menu</p> <p style=\\\"margin-left: 20px;\\\"><u>Direction</u> - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180&deg; (BUT MAKE SURE TO POWER OFF THE DEVICE!)</p> <p style=\\\"margin-left: 20px;\\\"><a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\"></a></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"72238e6a01d1152c\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"camera\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Camera Settings</h1> <p><strong>Jpeg quality</strong></p> <p style=\\\"margin-left: 20px;\\\">Value in percent, which usually does not need to be changed.</p> <p><strong>Downscale Preview</strong></p> <p style=\\\"margin-left: 20px;\\\">The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update</p> <p><strong>Image Rotation</strong></p> <p style=\\\"margin-left: 20px;\\\">Change the image rotation, if needed.</p> <p><strong>Timeout</strong></p> <p style=\\\"margin-left: 20px;\\\">Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.</p> <p><strong>Delay Before/After</strong></p> <p style=\\\"margin-left: 20px;\\\">A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.</p> <p><strong>AWBG, Gain, Contrast, Saturation</strong></p> <p style=\\\"margin-left: 20px;\\\">Under most circumstances, you do not need to touch these values.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"15a0a2f431ce55c3\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"General Settings\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"87a403b9a09aa38d\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Network\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"896242c5a7e50fa7\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstate = msg['payload']\\nmsg['state'] = state\\n\\nif state != load_str('model'):\\n    save('model', state)\\n    if state == 'OSMini':\\n        save('rotor_stepsperrotation',48000)\\n        save('cam_rotation',90)\\n        save('rotor_anglemin',-70)\\n        save('rotor_anglemax',20)\\n        \\n\\n    if state == 'OSClassic':\\n        save('rotor_stepsperrotation',17067)\\n        save('cam_rotation',0)\\n        save('rotor_anglemin',-30)\\n        save('rotor_anglemax',30)\\n\\nif state == \\\"OSMini\\\":\\n    msg['crop2'] = 'Crop X (%)'\\n    msg['crop1'] = 'Crop Y (%)'\\nelif state == \\\"OSClassic\\\":\\n    msg['crop1'] = 'Crop X (%)'\\n    msg['crop2'] = 'Crop Y (%)'\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"f358de1e64b491bb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4058a31e942e8f95\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"camera\",\n        \"func\": \"from OpenScan import load_str, save\\nfrom json import load\\nstate = msg['payload']\\nstate_old = load_str('camera')\\n\\nif state_old != state:\\n    save('camera',state)\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 540,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"34b685aff2080d31\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c833f6243a059d83\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"more sets\",\n        \"label\": \"Advanced Settings\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"8be8015931c663cc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"15fd1c9e5610cb85\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"more sets\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    msg['payload'] = False\\n    return None,msg\\n    \\nsave('advanced_settings', True)\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 820,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"62cd775a1c02dac8\"\n            ],\n            [\n                \"c833f6243a059d83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"74c5c7cd2681045b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"load camera&model\",\n        \"func\": \"from OpenScan import load_str, load_bool\\n\\nmodel = load_str('model')\\ncamera = load_str('camera')\\nupdate = load_bool('updateable')\\nmsg['model'] = model\\nmsg['camera'] = camera\\nmsg2 = {}\\nmsg3 = {}\\nmsg4 = {}\\n\\nif camera in ('imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','gphoto'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Arducam\\\"],\\\"show\\\":[\\\"Scan_Settings\\\",\\\"Scan_Picamera\\\"]}}\\nelif camera in ('imx519'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Picamera\\\"],\\\"show\\\":[\\\"Scan_Settings\\\",\\\"Scan_Arducam\\\"]}}\\nelif camera in ('external'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Arducam\\\",\\\"Scan_Picamera\\\"],\\\"show\\\":[\\\"Scan_Settings\\\"]}}\\n\\n\\nif model == 'None' or model == '' or camera == 'None' or camera == '':\\n    msg2['payload']={\\\"tabs\\\": {\\\"hide\\\": [\\\"Scan\\\", \\\"Files&Cloud\\\",\\\"Settings\\\",\\\"Update & Info\\\"]}}\\n    msg3['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"OpenScan_Home\\\"],\\\"show\\\":[\\\"OpenScan_Initialize\\\"]}}\\nelse:\\n    msg2['payload']={\\\"tabs\\\": {\\\"show\\\": [\\\"Scan\\\", \\\"Files&Cloud\\\",\\\"Settings\\\",\\\"Update & Info\\\"]},\\\"hide\\\":{}}\\n    msg3['payload'] = {\\\"group\\\":{\\\"show\\\":[\\\"OpenScan_Home\\\"],\\\"hide\\\":[\\\"OpenScan_Initialize\\\"]}}\\n\\nif update == True:\\n    msg4['payload'] = {\\\"group\\\":{\\\"show\\\":[\\\"OpenScan_Update\\\"]}}\\nelif update == False:\\n    msg4['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"OpenScan_Update\\\"]}}\\n\\nreturn msg,msg2,msg3,msg4\",\n        \"outputs\": 4,\n        \"x\": 340,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b4db790aad28ba39\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"change visibility\",\n        \"events\": \"all\",\n        \"x\": 600,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"eb8ccf2786ea3d63\",\n        \"type\": \"inject\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"1s_repeater\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"2\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 150,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"74c5c7cd2681045b\",\n                \"9b756a1f9b0e7317\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b756a1f9b0e7317\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"load advanced\",\n        \"func\": \"from OpenScan import load_bool\\n\\nif load_bool('advanced_settings') == False:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\"]}}\\nelse:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\",\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",]}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 320,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"b4db790aad28ba39\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ca4afadb5b21751f\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Info Texts\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f393400.d87dcc\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"check ip address\",\n        \"func\": \"import socket\\nimport subprocess\\n\\ntestIP = \\\"8.8.8.8\\\"\\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\\ns.connect((testIP, 0))\\nipaddr = s.getsockname()[0]\\nhost = socket.gethostname()\\n\\nmsg['ip']=ipaddr\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 410,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"bb789eed.9f73c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bb789eed.9f73c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Your local IP:\",\n        \"format\": \"{{msg.ip}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 590,\n        \"y\": 1060,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2a0f9919.4c9a86\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OpenScanCloud\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 1240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"27c6b221c90ed9e1\",\n        \"type\": \"exec\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"command\": \"iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\\\"//g;s/^ *//;s/ *$//'\",\n        \"addpay\": false,\n        \"append\": \"\",\n        \"useSpawn\": \"false\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"scan\",\n        \"x\": 250,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"b05cf92302a5c112\",\n                \"f393400.d87dcc\"\n            ],\n            [\n                \"e9677b85856b5873\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"b05cf92302a5c112\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"WIFI\",\n        \"func\": \"msg['options']=[]\\n\\nfor i in msg['payload'].split('\\\\n'):\\n    if i not in msg['options'] and i!=\\\"\\\":\\n        msg['options'].append(i)\\n        \\nif len(msg['options']) != 0:\\n    msg['enabled']=True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 370,\n        \"y\": 1020,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da5ddaf4cc25b8c8\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"search\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Search Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\",\n                \"51521bc6eb44cde5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"59c9f67283ba1709\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [],\n        \"payload\": \"\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"className\": \"\",\n        \"x\": 520,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"2bb52656f9554dab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2d7d6a730f7dca6\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Reset Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)\",\n        \"payloadType\": \"str\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"78985ac6d3bcdf60\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c3b8faac9ebb2c80\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"func\": \"from time import sleep\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('wpa_cli -i wlan0 reconfigure')\\nsleep(3)\\nos.system('systemctl restart nodered')\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 440,\n        \"y\": 1140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"78985ac6d3bcdf60\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 270,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"c3b8faac9ebb2c80\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f7f49b12c2d2572\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"add Wifi\",\n        \"func\": \"from time import sleep\\nsleep(0.1)\\n\\nos.system('wpa_cli -i wlan0 reconfigure')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 1320,\n        \"y\": 1000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ebcc98685059b9d4\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"password\",\n        \"x\": 780,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"68204a14528ab842\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"68204a14528ab842\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['password'] = msg['payload']\\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: <a href=\\\"https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\\\">Wikipedia</a>)'\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 910,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"852edf901bdec9c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"852edf901bdec9c5\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Save\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"country\",\n        \"x\": 1040,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"1b09d634e3d9357b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1b09d634e3d9357b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"modWPA\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nif len(msg['payload'])!=2:\\n    msg['payload'] = 'invalid country code'\\n    return msg,None\\n\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\\n\\ncode = msg['payload'].upper()\\nssid = msg['ssid']\\npassword = msg['password']\\n\\nif len(code) != 2:\\n    msg['topic'] = 'ERROR'\\n    msg['payload'] = 'invalid country code (see  ISO 3166-1 alpha-2)'\\n    return msg\\n\\nwith open(wpa_dir, 'r') as file:\\n    for i in file.readlines():\\n        if 'country=' in i:\\n            code_old=i.split('country=')[1][0:2]\\n            break\\n\\nwith open(wpa_dir, 'r') as file:\\n    wpa = file.read()\\n    if ssid in wpa:\\n        msg['topic'] = 'ERROR'\\n        msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\\n        return msg\\n    wpa=wpa.replace('country=' + code_old, 'country=' + code)\\n    wpa=wpa + '\\\\nnetwork={\\\\n priority=10\\\\n ssid=\\\"'+ssid+'\\\"\\\\n psk=\\\"'+password+'\\\"\\\\n}\\\\n'\\n\\nwith open(temp_dir,'w+') as file:\\n    file.write(wpa)\\nos.system('mv '+temp_dir + ' ' + wpa_dir)\\n\\nmsg['topic'] = 'Updating Wifi'\\nmsg['payload'] = 'reconnecting might take a moment'\\nreturn msg,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 1180,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ],\n            [\n                \"4f7f49b12c2d2572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"03732a7d3b0c95aa\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1330,\n        \"y\": 960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e97d17c6590138e2\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Cloud-settings\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>OpenScanCloud</h1> <p style=\\\"margin-left: 20px;\\\">OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at <a href=\\\"%5C%22https://www.buymeacoffee.com/OpenScan%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>BuyMeACoffee</a>.</p> <p style=\\\"margin-left: 20px;\\\">The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.</p> <p><strong>Register</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.</p> <p><strong>Enter Token</strong></p> <p style=\\\"margin-left: 20px;\\\">Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Token</strong></p> <p style=\\\"margin-left: 20px;\\\">A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Credit (GB)</strong></p> <p style=\\\"margin-left: 20px;\\\">Each token comes with a given amount of &apos;credit&apos; which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers.&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">IMPORTANT: The credit can be increased at any time by sending a (nice) mail to <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 620,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f7bf47e3eec6d736\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Number of Photos:\",\n        \"format\": \"{{msg.limit_photos}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b52d91c628b151a4\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Filesize (GB):\",\n        \"format\": \"{{msg.limit_filesize}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 1420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1969c709ef2fd1d5\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Credit (GB):\",\n        \"format\": \"{{msg.credit}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 1460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"88e92b621d2a3394\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"save\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import save, OpenScanCloud\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return None,msg\\n\\ntry:\\n    r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\\n    if r.status_code != 200:\\n        msg['payload'] = 'Could not verify token'\\n        return msg \\n    \\n    msg1 = r.json()\\n    \\n    save('osc_credit',msg1['credit'])\\n    save('osc_limit_filesize',msg1['limit_filesize'])\\n    save('osc_limit_photos',msg1['limit_photos'])\\n    msg1['enabled'] = True\\nexcept:\\n    pass\\n\\nsave('token',msg['token'])\\n    \\nmsg['payload'] = 'Token verified and saved'\\nreturn msg, msg1\",\n        \"outputs\": 2,\n        \"x\": 750,\n        \"y\": 1320,\n        \"wires\": [\n            [\n                \"76acd48a511a5e3e\",\n                \"b01581296b94dfcd\"\n            ],\n            [\n                \"9c51aa678f16980f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"76acd48a511a5e3e\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 890,\n        \"y\": 1280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"5f50ed3f6ba37cef\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Token\",\n        \"tooltip\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 350,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"cb62d30728af2968\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb62d30728af2968\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Save?\",\n        \"func\": \"msg['token'] = msg['payload']\\n\\nif len(msg['payload'])>=14:\\n    \\n    msg[\\\"payload\\\"]='Save and verify token: ' + msg['payload']\\n    return msg\\nelse:\\n    return None,msg\",\n        \"outputs\": 2,\n        \"x\": 470,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"94e503dd2e64d903\"\n            ],\n            [\n                \"d859bb39914d4999\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0dd01eef6e70059e\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"text\",\n        \"func\": \"from OpenScan import load_str\\n\\ntoken = load_str('token')[0:8]\\nmsg['payload']= token + '...'\\nif len(token)==0:\\n    msg['payload']=\\\"enter token\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"5f50ed3f6ba37cef\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"788fabff98c7973c\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"token\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"b01581296b94dfcd\",\n            \"d859bb39914d4999\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 75,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"0dd01eef6e70059e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"94e503dd2e64d903\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 610,\n        \"y\": 1320,\n        \"wires\": [\n            [\n                \"88e92b621d2a3394\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d859bb39914d4999\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"788fabff98c7973c\"\n        ],\n        \"x\": 555,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9c51aa678f16980f\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"links\": [\n            \"5d267acc10020091\",\n            \"397ab7f44b893c89\"\n        ],\n        \"x\": 835,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"397ab7f44b893c89\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSCparameters\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"9c51aa678f16980f\",\n            \"b42e061fb1f1f3d7\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 75,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"a7fd00943edc380b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b01581296b94dfcd\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"links\": [\n            \"788fabff98c7973c\",\n            \"5d267acc10020091\"\n        ],\n        \"x\": 835,\n        \"y\": 1320,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bf6d941ad307ce22\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 250,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"f22dfef37d5de773\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f22dfef37d5de773\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import re\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmail = msg['payload']\\nemail_regex = re.compile(r\\\"[^@]+@[^@]+\\\\.[^@]+\\\")\\n\\nif email_regex.match(mail) != None:\\n    msg['mail'] = mail\\n    msg['topic'] = 'OpenScanCloud Registration (2/3)'\\n    msg['payload'] = 'Enter your first name'\\n    return msg\\nmsg['payload'] = 'invalid input'\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 390,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"54602ee49ca022e7\"\n            ],\n            [\n                \"1505f3e72f971081\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1505f3e72f971081\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1560,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"54602ee49ca022e7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"f9efcb87b74abbd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"510dbe4d76253bd6\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"SUBMIT\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 810,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"600b2306caed1640\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"600b2306caed1640\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import OpenScanCloud\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['lastname'] = msg['payload']\\n\\nmsg2 = {}\\n\\nfor i in ['forename','lastname','mail']:\\n    msg2[i] = msg[i]\\n\\nr = OpenScanCloud('requestToken',msg2)\\n\\nstatus = r.status_code\\n\\nmsg['topic'] = 'OpenScanCloud Registration - Success'\\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\\n\\nif status != 200:\\n    msg['topic'] = 'OpenScanCloud Registration - Failed'\\n    msg['payload'] = 'Registration failed, please try again.'\\n\\nmsg['status'] = status\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 950,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"bbad1ab5f8f63fb7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d34cd203725bac15\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Register\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Register\",\n        \"tooltip\": \"testtesttest\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Please enter your email address:\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Requesting an OpenScanCloud Token\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"bf6d941ad307ce22\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bbad1ab5f8f63fb7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1090,\n        \"y\": 1520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a7fd00943edc380b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import load_int\\n\\nmsg = {}\\n\\ntry:\\n    msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\\n    msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\\n    msg['limit_photos'] = load_int('osc_limit_photos')\\n    return msg\\nexcept:\\n    pass\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"f7bf47e3eec6d736\",\n                \"b52d91c628b151a4\",\n                \"1969c709ef2fd1d5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"124459147143ec6a\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Motor\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 1600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dbd62b91a6c9c412\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 2240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"842b6fe016087ce3\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Pinout\",\n        \"info\": \"\",\n        \"x\": 110,\n        \"y\": 2860,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8c1a92f2dcc976c7\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Rotor_delay (ms)\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 1840,\n        \"wires\": [\n            [\n                \"bb54bbdae6690576\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2647111c06f2055d\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt delay\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 27,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2080,\n        \"wires\": [\n            [\n                \"fb8145a9f8d4f7b2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f9b51424edb0491c\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 1880,\n        \"wires\": [\n            [\n                \"ea87ecfd2af3cc7f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ab34b0a78b2c577\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"100\",\n        \"max\": \"5000\",\n        \"step\": \"100\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1920,\n        \"wires\": [\n            [\n                \"249f44c3a87793ba\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1d4230b3d9b93f63\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 460,\n        \"y\": 1800,\n        \"wires\": [\n            [\n                \"0bb56b1edb12c2cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e3222f0aba88040\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Accramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9d50311679acf215\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_Steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 810,\n        \"y\": 1920,\n        \"wires\": []\n    },\n    {\n        \"id\": \"25d7b4dd2aab8f05\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 1840,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15682cca9622831f\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 1800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8e2d22042bfcb4e8\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 23,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"tt\",\n        \"label\": \"TURNTABLE\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 2040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"56bc3b93af2ebe16\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 29,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2120,\n        \"wires\": [\n            [\n                \"35422077b53da9bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6ef996f8a36f94c2\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 31,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"500\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2160,\n        \"wires\": [\n            [\n                \"2c000bd53cdb98ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0c50fdbb5ac3c373\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 25,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 450,\n        \"y\": 2040,\n        \"wires\": [\n            [\n                \"485a4bed5a6bea23\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"213ccfb441a42890\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 30,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ttAccramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"73c9b4d09dc25e54\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 24,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 800,\n        \"y\": 2040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a81824c92f22487d\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 28,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9715161858f69649\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 26,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2080,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1b3ac50d2c6600c6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 1960,\n        \"wires\": [\n            [\n                \"e0d7c36daa42b3f3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6dcd1f0ccb01a299\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 1960,\n        \"wires\": []\n    },\n    {\n        \"id\": \"16e9a3a71c4bb916\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 33,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2200,\n        \"wires\": [\n            [\n                \"c34111aaec734dd9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"888161059eb9c71c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 32,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2200,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f4fc72297074c7ae\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"rotor\",\n        \"label\": \"ROTOR\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 1680,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b1d8f9e21b34102\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 35,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2240,\n        \"wires\": [\n            [\n                \"89dbbe7d99ddbbaf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2e839fe47a32b5f\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 22,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2000,\n        \"wires\": [\n            [\n                \"204b0a5c8629d78a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4519daf0b4b28aef\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 34,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5f269ea2c8a53f6c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 21,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b67dfacfc9a23aa5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"advanced settings\",\n        \"links\": [\n            \"62cd775a1c02dac8\"\n        ],\n        \"x\": 95,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"9b756a1f9b0e7317\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd775a1c02dac8\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"b67dfacfc9a23aa5\"\n        ],\n        \"x\": 955,\n        \"y\": 660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9d94dbc523d989a3\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_delay_after\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"b81e238ccd0a04fe\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0558d6eb9a01862e\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_delay_before\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"a0048747e7300bdc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d47515c9b208bfb7\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_timeout\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"1\",\n        \"step\": \"0.01\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"9b0d5c521a7822cc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"89c76766c7552b57\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_gain\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 22,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2540,\n        \"wires\": [\n            [\n                \"9b26ed02296d27c9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c385518eb65a1b27\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_awbg_red\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-10\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2580,\n        \"wires\": [\n            [\n                \"b0ac7e9a7c713b84\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c80833b718d9bf6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_awbg_blue\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-10\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2620,\n        \"wires\": [\n            [\n                \"827b1a671a77037d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5a3826e112fb24e6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_contrast\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 24,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2660,\n        \"wires\": [\n            [\n                \"78a1536c167da741\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3182ed7ac02b1509\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_saturation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 26,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2700,\n        \"wires\": [\n            [\n                \"fe9a5b68fc8c2077\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7fa6337cdf0a0bc8\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_jpeg_q\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"100\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2740,\n        \"wires\": [\n            [\n                \"e27d2613e942f344\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"08275bf96f87b8ef\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"timeout\",\n        \"label\": \"Timeout\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d2d028df4a139f41\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_after\",\n        \"label\": \"Delay after\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c6a65762aa4ffb7b\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_before\",\n        \"label\": \"Delay before\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"780323fd4504b855\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 21,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"gain\",\n        \"label\": \"Gain\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"780bf08b41202135\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"awbg red\",\n        \"label\": \"AWBG red\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c0faf441fc918538\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"awbg blue\",\n        \"label\": \"AWBG blue\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"93d12b447a39c5bb\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 23,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"contrast\",\n        \"label\": \"Contrast\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e77e6dcd285d3062\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 25,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"saturation\",\n        \"label\": \"Saturation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a7075bc8d5ee1138\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"jpegQ\",\n        \"label\": \"Jpeg Quality\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2740,\n        \"wires\": []\n    },\n    {\n        \"id\": \"282681e7c4351f74\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ext\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 3,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"b17e82651407d8e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da43c58979737fec\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 2,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ext\",\n        \"label\": \"External Camera\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ef70d61678fe1f11\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"light1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2940,\n        \"wires\": [\n            [\n                \"2c812acffdb330c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fec56a7e913b21d6\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 4,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light1\",\n        \"label\": \"Light 1\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"24929b4629f22070\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"light2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2980,\n        \"wires\": [\n            [\n                \"ae0654af69446942\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7c6bdc0504aa4cc7\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 6,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light2\",\n        \"label\": \"Light 2\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2980,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8c396b060f3d2646\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotordir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3020,\n        \"wires\": [\n            [\n                \"58cf48cfacc979fb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"97568610daccf74a\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 8,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotordir\",\n        \"label\": \"Rotor direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3020,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a3c58ea48c388215\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotorstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3060,\n        \"wires\": [\n            [\n                \"c7ae206f2fff6810\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6da92aeaeffd95e0\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 10,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotorstep\",\n        \"label\": \"Rotor step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3060,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b5da90eaf6ac562\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotoren\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 13,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3100,\n        \"wires\": [\n            [\n                \"cfebd4a47a68b319\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"12623e4addfa2c22\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 12,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotoren\",\n        \"label\": \"Rotor enable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f24cb404d7d09f8a\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ttdir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 15,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 3140,\n        \"wires\": [\n            [\n                \"90f4d220928e4727\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"542bfb9d92935c2c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 14,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttdir\",\n        \"label\": \"Turntable direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 3140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1f79467df98ce894\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ttstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 17,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 3180,\n        \"wires\": [\n            [\n                \"b05e1e612887f9c2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"170d3b925f7745cc\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 16,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttstep\",\n        \"label\": \"Turntable step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 3180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"661614f5bd2c71d6\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"endstop1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 21,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3220,\n        \"wires\": [\n            [\n                \"2af447a6905b83bc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c18b55859dae5f85\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 20,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop1\",\n        \"label\": \"Endstop Rotor\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e23a396162026618\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"endstop2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 23,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3260,\n        \"wires\": [\n            [\n                \"787a128f84f747c0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"82c1a33014d003e9\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 22,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop2\",\n        \"label\": \"Endstop Turntable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5255759a7c5b2a74\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"confirm\",\n        \"x\": 680,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"15fd1c9e5610cb85\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8be8015931c663cc\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import save, load_bool\\n\\nif msg['payload'] == True and not load_bool('advanced_settings'):\\n    msg['payload'] = '''<p style=\\\"text-align: center;\\\"><span style=\\\"font-size: 22px;\\\"><strong><u>PLEASE READ :)</u></strong></span></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Modifying the advanced settings can potentially damage your device and/or the connected peripherals.</strong></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Please read the given information texts carefully and only change settings, when you are sure about the consequences!</strong></p>\\n'''\\n    return msg\\nelif not msg['payload']: \\n    save('advanced_settings', False)\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"5255759a7c5b2a74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9d464b2ba1edaf48\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_rotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"270\",\n        \"step\": \"90\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2780,\n        \"wires\": [\n            [\n                \"b7d3fe0c0b40b3e1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"db98b95693ebce63\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cam_rot\",\n        \"label\": \"Image Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2780,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6659121906897a1f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1800,\n        \"wires\": [\n            [\n                \"1d4230b3d9b93f63\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0bb56b1edb12c2cf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1800,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"569829eeff715c33\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1920,\n        \"wires\": [\n            [\n                \"1ab34b0a78b2c577\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"249f44c3a87793ba\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1920,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c997e60519341afd\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1960,\n        \"wires\": [\n            [\n                \"1b3ac50d2c6600c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e0d7c36daa42b3f3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"59ecf3a22cd3a669\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2000,\n        \"wires\": [\n            [\n                \"b2e839fe47a32b5f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"204b0a5c8629d78a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"15f02421b30a9ab6\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1840,\n        \"wires\": [\n            [\n                \"8c1a92f2dcc976c7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bb54bbdae6690576\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1840,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"58928befcc61b1f7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1880,\n        \"wires\": [\n            [\n                \"f9b51424edb0491c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ea87ecfd2af3cc7f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1880,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"27bc56f273360ac7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nsteps = parseInt(data);\\nif (steps == 3600){\\n    steps = 3200\\n}\\n\\nmsg.payload = steps\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2040,\n        \"wires\": [\n            [\n                \"0c50fdbb5ac3c373\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f46ced86106306c8\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2160,\n        \"wires\": [\n            [\n                \"6ef996f8a36f94c2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4339704cd8552eb3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2200,\n        \"wires\": [\n            [\n                \"16e9a3a71c4bb916\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ac53bb6150645fe\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2240,\n        \"wires\": [\n            [\n                \"9b1d8f9e21b34102\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b89eb1eaf333c10\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2080,\n        \"wires\": [\n            [\n                \"2647111c06f2055d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e8927be0e235fa1\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2120,\n        \"wires\": [\n            [\n                \"56bc3b93af2ebe16\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"485a4bed5a6bea23\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2040,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2c000bd53cdb98ca\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2160,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c34111aaec734dd9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"89dbbe7d99ddbbaf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2240,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fb8145a9f8d4f7b2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2080,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"35422077b53da9bf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d5308090f2b7971a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_timeout'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"d47515c9b208bfb7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b0d5c521a7822cc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_timeout'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2420,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"694d1068bea15171\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"9d94dbc523d989a3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cec3e5e78a40476b\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"0558d6eb9a01862e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b81e238ccd0a04fe\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a0048747e7300bdc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6f524f9370a18482\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2540,\n        \"wires\": [\n            [\n                \"89c76766c7552b57\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b26ed02296d27c9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1f87f473e327c3cc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_awbg_red'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2580,\n        \"wires\": [\n            [\n                \"c385518eb65a1b27\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b0ac7e9a7c713b84\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_awbg_red'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cff7ac5f1e061855\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_awbg_blue'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2620,\n        \"wires\": [\n            [\n                \"5c80833b718d9bf6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"827b1a671a77037d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_awbg_blue'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cf854461c37ca54f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2660,\n        \"wires\": [\n            [\n                \"5a3826e112fb24e6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"78a1536c167da741\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ba10e04dd1761692\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2700,\n        \"wires\": [\n            [\n                \"3182ed7ac02b1509\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fe9a5b68fc8c2077\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2700,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a69d216114f908a5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2740,\n        \"wires\": [\n            [\n                \"7fa6337cdf0a0bc8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e27d2613e942f344\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2740,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f02d4a036a225e87\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2780,\n        \"wires\": [\n            [\n                \"9d464b2ba1edaf48\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b7d3fe0c0b40b3e1\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2780,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"612cccacda1a65aa\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"282681e7c4351f74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b17e82651407d8e0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2900,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"3b126549c03a872e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3220,\n        \"wires\": [\n            [\n                \"661614f5bd2c71d6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2af447a6905b83bc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"954db931f87894ee\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2940,\n        \"wires\": [\n            [\n                \"ef70d61678fe1f11\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2c812acffdb330c5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2940,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6682c8057e89d087\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2980,\n        \"wires\": [\n            [\n                \"24929b4629f22070\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ae0654af69446942\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2980,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"015be401d08047d2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3020,\n        \"wires\": [\n            [\n                \"8c396b060f3d2646\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"58cf48cfacc979fb\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3020,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1c6c0f8b9ac95659\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3060,\n        \"wires\": [\n            [\n                \"a3c58ea48c388215\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c7ae206f2fff6810\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3060,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"dcee66c0d56c6934\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3100,\n        \"wires\": [\n            [\n                \"9b5da90eaf6ac562\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cfebd4a47a68b319\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ec7d85bb17eb159\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3140,\n        \"wires\": [\n            [\n                \"f24cb404d7d09f8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f42d02a3776a006\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3180,\n        \"wires\": [\n            [\n                \"1f79467df98ce894\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"90f4d220928e4727\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"b05e1e612887f9c2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3180,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"58bbe9fc41e0d7b9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3260,\n        \"wires\": [\n            [\n                \"e23a396162026618\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"787a128f84f747c0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"78351089ee9ebeaf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'ssh'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"40dee936a9abac0d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5fba78ae65eaaf5d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'smb'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"4fd9bb53fdb51a25\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67206663b3881868\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'advanced_settings'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"c833f6243a059d83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3492754252645e62\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'camera'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data.replace(/(\\\\r\\\\n|\\\\n|\\\\r)/gm,\\\"\\\"));\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"a2c1dba3e67be015\",\n                \"6f3d403e157163e4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d16525a31223bc42\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'model'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"80b579a4220e5c23\",\n                \"c6138801b30f091d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f99ec8781a33ec7d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'This is a free piece of software and it is provided as is, without any warranty. <br>There might be functions that need a connection to the internet: '+\\n    '<br><br>By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP  to my servers '+\\n    '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days  The images might be used for further experiments (e.g. machine learning, automation ...). '+\\n    '<br><br>By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\\n    'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\\n    'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\\n    '<br><br>By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\\n    'The raw images and resulting 3d models will never be published without your explicit consent.'+  \\n    '<br><br>If you have any questions you can contact me at info@openscan.eu.'+ \\n    '<br><br>THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\\n    'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \\n    'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \\n    'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"7dc39bd847d16ded\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5f849178998d9082\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"if(msg.payload === 'Agree'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nvar file = 'terms'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nfs.writeFile(filepath+file, String(data), err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"725fd0cab0bddc0e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'hostname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"49259adad52fc214\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"49259adad52fc214\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Hostname\",\n        \"tooltip\": \"\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"Change hostname to:\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 530,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"8001f7c361de7d8c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51521bc6eb44cde5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb52656f9554dab\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"ssid = msg.payload\\nmsg.topic = 'Add wifi network (' + ssid + ')'\\nmsg.payload = 'Enter Wifi password:'\\nmsg.ssid = ssid\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 650,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"ebcc98685059b9d4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ebce67b739d1891f\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk/change hostname\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    pass\\n\\nwith open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\n\\nhostname = msg['hostname']\\nif len(hostname) < 4 :\\n    msg['payload'] = ' '\\n    msg['topic'] = 'ERROR - Hostname NOT changed'\\n    return msg\\n    \\n\\nwith open('/etc/hostname', 'w+') as file:\\n    file.write(hostname)\\nos.system('echo ' + hostname + ' | tee /etc/hostname')\\nwith open('/etc/hosts', 'r') as file:\\n    temp = file.read()\\ntemp = temp.replace(old_hostname,hostname)\\nwith open('/etc/hosts', 'w') as file:\\n    file.write(temp)\\nos.system('hostnamectl set-hostname ' + hostname)\\nos.system('systemctl restart avahi-daemon')\\nsave('hostname',hostname)\\nmsg['payload'] = hostname\\nmsg['topic'] = 'Success - Hostname changed'\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 1140,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"667ac2aba819f506\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Confirm\",\n        \"x\": 920,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8001f7c361de7d8c\",\n        \"type\": \"change\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"hostname\",\n                \"pt\": \"msg\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 710,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"667ac2aba819f506\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9bb0adbd716ce347\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"links\": [\n            \"16c76929f88df841\",\n            \"fe3a855fee9e28c6\"\n        ],\n        \"x\": 155,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"d114f4d4d7f31981\",\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f9efcb87b74abbd4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload === 'Cancel'){\\n    return\\n}\\nmsg.forename = msg.payload\\nmsg.topic = 'OpenScanCloud Registration (3/3)'\\nmsg.payload = 'Enter your last name'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 670,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"510dbe4d76253bd6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"adc206aa8edd1e41\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSC\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Files&amp;Cloud</h1> <p><strong>Refresh</strong></p> <p style=\\\"margin-left: 20px;\\\">You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.</p> <p style=\\\"margin-left: 20px;\\\">The status (in the table) of the individual sets in the file list will be updated to one of the following:</p> <p style=\\\"margin-left: 20px;\\\"><u>Created&nbsp;</u>- you started the upload of your image set. If you are stuck on this status, please try to restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Initialized&nbsp;</u>- all files have been uploaded and processing will start as soon as possible</p> <p style=\\\"margin-left: 20px;\\\"><u>File approved&nbsp;</u>- the server received and verified your files</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing started</u> - your files are currently being processed</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing failed</u> - there are various reasons why processing might fail. Please check the email for more details or contact me at <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p style=\\\"margin-left: 20px;\\\">processing done - check your email, where you should find a link to the 3d model :)</p> <p><strong>Status (on the right column)</strong></p> <p style=\\\"margin-left: 20px;\\\">Indicates, what the device is currently up to.</p> <p style=\\\"margin-left: 20px;\\\"><u>Refreshing&nbsp;</u>- updating all image set&apos;s status</p> <p style=\\\"margin-left: 20px;\\\"><u>Uploading&nbsp;</u>- while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Project started</u> - when the upload of a set was successful</p> <p style=\\\"margin-left: 20px;\\\"><u>Zipping&nbsp;</u>- files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.</p> <p style=\\\"margin-left: 20px;\\\"><u>Combining</u> - two sets into one might take up to a minute. </p> <p><strong>Set</strong></p> <p style=\\\"margin-left: 20px;\\\">select a set from the file list by clicking on a row in the table</p> <p><strong>Download</strong></p> <p style=\\\"margin-left: 20px;\\\">Download the selected set from the OpenScan device to your computer/mobile/tablet</p> <p><strong>Upload</strong></p> <p style=\\\"margin-left: 20px;\\\">Upload the selected file to the OpenScanCloud</p> <p><strong>Combine</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!</p> <p><strong>Delete Set/All</strong></p> <p style=\\\"margin-left: 20px;\\\">Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 590,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"45df91cae421e8e1\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Scan_settings\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Scan Settings</h1> <p><strong>Current Status</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>--READY--&nbsp;</u>- everything is okay and ready to go :)</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-preparing</u> - before starting the routine some time might pass depending on the number of photos</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-stopping</u> - manually ending the routine by pressing the stop button</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-Photo X/Y</u> - Showing the progress of the routine</p> <p style=\\\"margin-left: 20px;\\\"><u>No Camera Found</u> - please check the camera ribbon cable</p> <p style=\\\"margin-left: 20px;\\\"><u>Error: XXX</u> - Please contact <a href=\\\"mailto:info@openscan.eu\\\">info@openscan.eu</a> or post an issue on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a></p> <p><strong>Projectname</strong></p> <p style=\\\"margin-left: 20px;\\\">Each photo set will be saved using the following pattern &nbsp;YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified &apos;default&apos; will be used.</p> <p><strong>Rotor</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the rotor by increments of 5&deg;. Please make sure to start the routine with the camera in the horizontal position.</p> <p><strong>Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the turntable by increments of 15&deg;.</p> <p><strong>Ringlight</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.</p> <p><strong>Photos</strong></p> <p style=\\\"margin-left: 20px;\\\">Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. <u><strong>If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!</strong></u></p> <p><strong>Shutter</strong></p> <p style=\\\"margin-left: 20px;\\\">Again: <strong><u>Less is more</u></strong>! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:</p> <p style=\\\"margin-left: 20px;\\\">- no polarizer: 5-20ms</p> <p style=\\\"margin-left: 20px;\\\">- mostly white object, &nbsp;with polarizer + one ringlight: 50-200ms</p> <p><strong>Crop X/Y</strong></p> <p style=\\\"margin-left: 20px;\\\">Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!</p> <p><strong>Start/Stop</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the buttons to start/stop the routine</p> <p><strong>Reboot/Shutdown</strong></p> <p style=\\\"margin-left: 20px;\\\">In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 760,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e9677b85856b5873\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset rfkill\",\n        \"func\": \"from os import system\\nif \\\"Interface doesn't support scanning\\\" in msg['payload']:\\n    system('rfkill unblock all')\\n    system('ifconfig wlan0 up')\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 390,\n        \"y\": 1100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"91fe20cb16f54293\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1680,\n        \"wires\": [\n            [\n                \"327c8bdde31033a4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"add3e998b097c54f\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"Min Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1680,\n        \"wires\": []\n    },\n    {\n        \"id\": \"da286366433c83a0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1680,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"327c8bdde31033a4\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1680,\n        \"wires\": [\n            [\n                \"da286366433c83a0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"94288df4c6756197\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"e531ffe3dcf34eb4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4702a4a09124e27d\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"9ce407cb16f0419a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2cf946c7aab2cbb4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"94288df4c6756197\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e531ffe3dcf34eb4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4da5f650d3845baa\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"4702a4a09124e27d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ce407cb16f0419a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fda776c5aa642867\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"Max Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6e9af48a1c4c58c6\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"Start Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1760,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b2bc9849aee310b\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"changeHostname\",\n        \"links\": [\n            \"ec2db55a99bbe3ee\",\n            \"d5175561293ef490\",\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 835,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"8b9e3781511e9231\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8b9e3781511e9231\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk\",\n        \"func\": \"with open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\nif old_hostname == 'raspberrypi':\\n    msg['hostname'] = 'openscan'\\n    msg['payload'] = 'OK'\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3fcbd9fe3acc3fb7\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"scan_arducam\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 1,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Focus Settings</h1> <p><strong>MF - Manual Focus</strong></p> <p style=\\\"margin-left: 20px;\\\">By default, the switch is &apos;off&apos;, which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object). Be aware, that the distance value is only a rough estimate (mm)</p> <p><strong>ST - Stacking</strong></p> <p style=\\\"margin-left: 20px;\\\">Stacking is disabled by default. Once activated, you will be able to set the following:</p> <p style=\\\"margin-left: 20px;\\\"><u>Stacksize</u> - defines the number of photos between the minimal and the maximal focal distance</p> <p style=\\\"margin-left: 20px;\\\"><u>SET</u> press this button to set the maximal/minimal focal distance. Pressing the button a third time will re-set the values.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 760,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d68cccec646e0a0\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable routine\",\n        \"func\": \"msg_enable = {}\\nmsg_disable = {}\\n\\nmsg_enable['enabled'] = True\\nmsg_disable['enabled'] = False\\n\\nif msg['payload'] == 'external':\\n    return msg_enable, msg_disable\\nif msg['payload'] == 'gphoto':\\n    return msg_enable, msg_enable, msg_disable\\n\\nreturn msg_enable\",\n        \"outputs\": 3,\n        \"x\": 560,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"a0ba1aa77c5c8b7c\"\n            ],\n            [\n                \"a42c12e94f65fa01\"\n            ],\n            [\n                \"2d76e5617f13cd6c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a0ba1aa77c5c8b7c\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2aea1727dbea76ce\",\n            \"4f212b44aa487945\",\n            \"65cef204b16f8741\",\n            \"917a194be245384a\"\n        ],\n        \"x\": 675,\n        \"y\": 420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a42c12e94f65fa01\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2aea1727dbea76ce\",\n            \"4f212b44aa487945\",\n            \"65cef204b16f8741\",\n            \"917a194be245384a\"\n        ],\n        \"x\": 715,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2d76e5617f13cd6c\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"65cef204b16f8741\"\n        ],\n        \"x\": 675,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bd80ec228fb9a86d\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 135,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"78351089ee9ebeaf\",\n                \"5fba78ae65eaaf5d\",\n                \"3492754252645e62\",\n                \"d16525a31223bc42\",\n                \"67206663b3881868\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65b38bfeb3fee710\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 155,\n        \"y\": 760,\n        \"wires\": [\n            [\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d3fc91d87d5d5f62\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 135,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"725fd0cab0bddc0e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cc9c4092edeb43cc\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 135,\n        \"y\": 1020,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\",\n                \"f393400.d87dcc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f0b355967b33dfee\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 175,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"91fe20cb16f54293\",\n                \"2cf946c7aab2cbb4\",\n                \"4da5f650d3845baa\",\n                \"6659121906897a1f\",\n                \"15f02421b30a9ab6\",\n                \"58928befcc61b1f7\",\n                \"569829eeff715c33\",\n                \"c997e60519341afd\",\n                \"59ecf3a22cd3a669\",\n                \"27bc56f273360ac7\",\n                \"9b89eb1eaf333c10\",\n                \"2e8927be0e235fa1\",\n                \"f46ced86106306c8\",\n                \"4339704cd8552eb3\",\n                \"1ac53bb6150645fe\",\n                \"0d48bb415c584420\",\n                \"b6e420121e6466e7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d7c1fb4c028b21a5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 155,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"d5308090f2b7971a\",\n                \"694d1068bea15171\",\n                \"cec3e5e78a40476b\",\n                \"6f524f9370a18482\",\n                \"1f87f473e327c3cc\",\n                \"cff7ac5f1e061855\",\n                \"cf854461c37ca54f\",\n                \"ba10e04dd1761692\",\n                \"a69d216114f908a5\",\n                \"f02d4a036a225e87\",\n                \"1efd4a05aee0b86c\",\n                \"6841e5a392f0fb4f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a67c18aaca2f5fa5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 155,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"612cccacda1a65aa\",\n                \"954db931f87894ee\",\n                \"6682c8057e89d087\",\n                \"015be401d08047d2\",\n                \"1c6c0f8b9ac95659\",\n                \"dcee66c0d56c6934\",\n                \"6ec7d85bb17eb159\",\n                \"4f42d02a3776a006\",\n                \"3b126549c03a872e\",\n                \"58bbe9fc41e0d7b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6d3821bc7f43f8e\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset default\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 14,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Restore default settings\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"This can not be undone!\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Restore default settings?\",\n        \"topicType\": \"str\",\n        \"x\": 930,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"e4be21c38b57f560\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e4be21c38b57f560\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1090,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"9f30de04ced693d3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9f30de04ced693d3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.overwrite = true\\nif(msg.payload == \\\"Yes\\\"){\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1230,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"80bccc884b0be297\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"80bccc884b0be297\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 1325,\n        \"y\": 300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"34b685aff2080d31\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"boot-cam\",\n        \"func\": \"from OpenScan import load_str\\n\\ncamera_modules = ('imx519', 'imx219', 'ov5647', 'imx477', 'imx378', 'ov9281', 'imx290a', 'imx290b')\\n\\npt1 = \\\"[all]\\\\n\\\\ncamera_auto_detect=0\\\\ngpu_mem=256\\\\ndtoverlay=vc4-fkms-v3d\\\\ndtoverlay=\\\"\\npt3 = \\\",media-controller=1\\\\n\\\"\\n\\nwith open('/boot/config.txt', 'r') as file:\\n    config = file.read()\\n\\ncamera = load_str('camera')\\nif camera not in camera_modules:\\n    msg['payload'] = 'no changes'\\n    return\\n\\nif camera == 'imx290a':\\n    camera = 'imx290,clock-frequency=37125000'\\nelif camera == 'imx290b':\\n    camera = 'imx290,clock-frequency=74250000'\\n\\nconfig_keep = config.split('[all]\\\\n')[0]\\nconfig_new = config_keep + pt1 + camera + pt3\\n\\nwith open('/boot/config.txt', 'w') as file:\\n    file.write(config_new)\\n\\nmsg['topic'] = 'Camera configuration changed'\\nmsg['payload'] = 'Please restart the device'\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 680,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"68cba0c530c6def6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"68cba0c530c6def6\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 830,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f304680180a23479\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Info\",\n        \"x\": 1010,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0d48bb415c584420\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1640,\n        \"wires\": [\n            [\n                \"ce215e159ce7267f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ce215e159ce7267f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Turntable Mode\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1640,\n        \"wires\": [\n            [\n                \"f95f528dec31425c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f95f528dec31425c\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4ebe5baece5ce9f2\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"preview_resolution\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.5\",\n        \"max\": \"10\",\n        \"step\": \"0.5\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"60a415fff23cb55e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ed0498cceceedde\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"preview_res\",\n        \"label\": \"Preview Resolution (Mpx)\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1efd4a05aee0b86c\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_preview_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data)/1000000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"4ebe5baece5ce9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"60a415fff23cb55e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_preview_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload*1000000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6f3d403e157163e4\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 5,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Pi Cam v1 - 5mp\",\n                \"value\": \"ov5647\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam v2 - 8mp\",\n                \"value\": \"imx219\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam HQ - 12.3mp\",\n                \"value\": \"imx477\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Arducam IMX519 - 16mp\",\n                \"value\": \"imx519\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 a\",\n                \"value\": \"imx290a\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 b\",\n                \"value\": \"imx290b\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX378\",\n                \"value\": \"imx378\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OV9281\",\n                \"value\": \"ov9281\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"DSLR (gphoto)\",\n                \"value\": \"gphoto\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"USB Webcam\",\n                \"value\": \"usb_webcam\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"External Camera\",\n                \"value\": \"external\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"6d68cccec646e0a0\",\n                \"4058a31e942e8f95\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6138801b30f091d\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 3,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Please Select\",\n                \"value\": \"None\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Mini\",\n                \"value\": \"OSMini\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Classic\",\n                \"value\": \"OSClassic\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"896242c5a7e50fa7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4da67c23c7a543a0\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 4,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Camera\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 840,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1fed8676078ea9a7\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 2,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Model\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a4b7eea9a9736b0a\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Update&Info\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Update&amp;Log</h1> <p><strong>Status</strong></p> <p>See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a> for details and the source code.</p> <p><strong>Updatetype</strong></p> <p>- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras</p> <p>- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well</p> <p>- mini: very simplified firmware for the OpenScanMini + Arducam IMX519</p> <p><strong>Auto-Check update availability</strong></p> <p>Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p> <p>This option is activated by default.</p> <p><strong>Check Updates</strong></p> <p>Alternatively, you can check for updates manually at any time by pressing this button.</p> <p><strong>Download Error Log</strong></p> <p>In case you encounter any errors with your device, please download the error log text and send a copy to <a href=\\\"%5C%22mailto:info@openscan.eu%5C%22\\\">info@openscan.eu</a> or create an issue on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 750,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b6e420121e6466e7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'routine_secondpass'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"ab8d5cfe9190bb5f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab8d5cfe9190bb5f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Second pass\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"fa51327f0140b045\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fa51327f0140b045\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_secondpass'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1600,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6841e5a392f0fb4f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'cam_output_downscale'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"110216d678fad14f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"110216d678fad14f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Downscale output\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"214d548d564f8ba2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"214d548d564f8ba2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_output_downscale'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\\n  \\nmsg.enabled = msg.payload\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"1becbff4884b8c1a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8be1ca844a6caa54\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"output_resolution\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.5\",\n        \"max\": \"20\",\n        \"step\": \"0.5\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2360,\n        \"wires\": [\n            [\n                \"a6b2c0a0604ccf14\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ac09d89d791e953\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"image_res\",\n        \"label\": \"Output Resolution (Mpx)\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1becbff4884b8c1a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_output_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data)/1000000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2360,\n        \"wires\": [\n            [\n                \"8be1ca844a6caa54\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a6b2c0a0604ccf14\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_output_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload*1000000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f358de1e64b491bb\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"b30d918661392ab3\",\n            \"44c598049cd533fd\"\n        ],\n        \"x\": 635,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b0629875a30ae1d7\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"get update\",\n        \"func\": \"import json\\nimport requests\\nfrom OpenScan import load_str\\n\\nif not msg['payload']:\\n    msg['status'] = '--READY--'\\n    return msg\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'no internet connection'\\n    return msg\\n\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg['status'] = 'checking updates'\\n\\nreturn msg, msg\",\n        \"outputs\": 2,\n        \"x\": 350,\n        \"y\": 240,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\"\n            ],\n            [\n                \"fefe45404bdb19c4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c7b6d05a62172432\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Status:\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 170,\n        \"y\": 100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"fefe45404bdb19c4\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"check files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str\\n\\nscope = load_str('update_type')\\nmsg['scope'] = scope\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_old = updatepath + 'update.json'\\n\\ncounter = 0\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    if os.path.isfile(temp):\\n        filesize = os.path.getsize(temp)\\n        msg[scope][i]['filesize2'] = filesize\\n        if filesize == msg[scope][i]['filesize']:\\n            msg[scope][i]['update'] = False\\n            continue\\n    msg[scope][i]['update'] = True\\n\\n    counter += 1\\n\\nif counter == 0:\\n    msg['status'] = 'No new update available'\\nelse:\\n    msg['status'] = 'New update available'\\n    msg['topic'] = msg['status']\\n    msg['payload'] = 'Install & reboot now?'\\n\\nmsg['counter'] = counter\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\",\n                \"ae92a328af306ebb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d0104e0163745993\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 75,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"ec30638407332e43\",\n                \"38cbf7965d1c1834\",\n                \"49f1ecb29a3f84f4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ec30638407332e43\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data.replace(/(\\\\r\\\\n|\\\\n|\\\\r)/gm,\\\"\\\"));\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"2852023f3aa8db10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2852023f3aa8db10\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"stable\",\n                \"value\": \"main\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"beta\",\n                \"value\": \"beta\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 300,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"1e10b387ee30c486\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1e10b387ee30c486\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 430,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"274129c51b0b87ef\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 4,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Updatetype: \",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 570,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"51cd8c8643e6b46a\",\n        \"type\": \"ui_switch\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"label\": \"Auto-check update availability\",\n        \"tooltip\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"1ab4c6b4b232a022\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38cbf7965d1c1834\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"51cd8c8643e6b46a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ab4c6b4b232a022\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 570,\n        \"y\": 140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ae92a328af306ebb\",\n        \"type\": \"ui_toast\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"NO\",\n        \"cancel\": \"YES\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 670,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"2de63e8e3ae5fb0c\",\n                \"929281fef53e09f8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cbd0afc4aa7b302a\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"update status\",\n        \"links\": [\n            \"1bbe2d769f42c313\",\n            \"42061b28cff81f99\"\n        ],\n        \"x\": 75,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"c7b6d05a62172432\",\n                \"c94623ddd9d95f78\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1bbe2d769f42c313\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 625,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7cf60615d93e696b\",\n        \"type\": \"ui_button\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 7,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Check Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 140,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2de63e8e3ae5fb0c\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"download files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str, save\\n\\n\\nscope = msg['scope']\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\n\\nif msg['payload'] != 'YES':\\n    return\\n\\ncounter = 0\\n\\nfor i in msg[scope]:\\n    if msg[scope][i]['update'] == False:\\n        continue\\n    \\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    \\n    r = requests.get(url + msg[scope][i]['src'])\\n    if r.status_code != 200:\\n        msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\\n        return msg\\n    with open(temp, 'wb+') as file:\\n        file.write(r.content)\\n    shutil.copy(temp, msg[scope][i]['dst'])\\n    \\n    if msg[scope][i]['dst'] == '/boot/config.txt':\\n        save('camera','')\\n    \\n    counter += 1\\n\\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\\n\\nif counter == msg['counter']:\\n    updatepath_temp = updatepath + 'update_temp.json'\\n    updatepath_old = updatepath + 'update.json'\\n    shutil.move(updatepath_temp, updatepath_old)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 840,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\",\n                \"fe3a855fee9e28c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"929281fef53e09f8\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload == 'YES'){\\n    msg.status = 'Installing updates'\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"42061b28cff81f99\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 955,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"49f1ecb29a3f84f4\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\n\\nmsg.payload = data\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fe3a855fee9e28c6\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9bb0adbd716ce347\"\n        ],\n        \"x\": 955,\n        \"y\": 260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5e7d5e4335d37794\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 55,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb5fe78e09fec8a\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"msg\",\n        \"func\": \"\\nfrom subprocess import getoutput\\nimport os\\n\\nmsg['os'] = getoutput(\\\"cat /etc/os-release | grep -i 'PRETTY_NAME'\\\")[13:-1]\\nmsg['device'] = getoutput(\\\"cat /proc/device-tree/model\\\")\\nmsg['flask'] = getoutput(\\\"systemctl status flask |grep -i 'Active:'\\\").split(' ')[6]\\nmsg['osdate'] = getoutput(\\\"vcgencmd version\\\").split('\\\\n')[0]\\nmsg['temp'] = getoutput(\\\"vcgencmd measure_temp\\\").split('=')[1]\\ncpu_total = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $2}'\\\")\\ncpu_used = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $3}'\\\")\\nswap_total = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $2}'\\\")\\nswap_used = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_used = getoutput(\\\"df -h / | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_total = getoutput(\\\"df -h / | tail -n1 |awk '{print $2}'\\\")\\n\\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\\n\\nif msg['flask'] == 'inactive':\\n    os.system('systemctl restart flask')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 170,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"dbc77052ac950624\",\n                \"d97c3068ef5fef96\",\n                \"73a3b828f862312b\",\n                \"901e31453b2bdff8\",\n                \"f983854748ee4763\",\n                \"5347c7c517f5e8c7\",\n                \"3a5016f7003cd72c\",\n                \"6d720c4a4ecd9475\",\n                \"6438b7d060a70d81\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d97c3068ef5fef96\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"OS:\",\n        \"format\": \"{{msg.os}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"73a3b828f862312b\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 8,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Flask:\",\n        \"format\": \"{{msg.flask}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dbc77052ac950624\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Device:\",\n        \"format\": \"{{msg.device}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 460,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4c7fa5b5b27b83a5\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"create beta new\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\n\\n#scope = 'main'\\nscope = 'beta'\\n\\nupdatepath = '/home/pi/OpenScan/tmp/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\n\\n## load update.json\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'download update.json failed'\\n    return msg\\n\\nmsg = {}\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg2 = msg.copy()\\ndel msg[scope]\\n\\nmsg[scope]={}\\nmsg[scope]['1'] = {}\\nmsg[scope]['1']['src'] = scope + '/fla.py'\\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\\n\\nmsg[scope]['2'] = {}\\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\\n\\nmsg[scope]['3'] = {}\\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\\n\\nmsg[scope]['4'] = {}\\nmsg[scope]['4']['src'] = scope + '/config.txt'\\nmsg[scope]['4']['dst'] = '/boot/config.txt'\\n\\nmsg[scope]['5'] = {}\\nmsg[scope]['5']['src'] = scope + '/flows.json'\\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\\n\\nmsg[scope]['6'] = {}\\nmsg[scope]['6']['src'] = scope + '/settings.js'\\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\\n\\nmsg[scope]['7'] = {}\\nmsg[scope]['7']['src'] = 'files/logo.jpg'\\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\\n\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    filesize = os.path.getsize(filepath)\\n    msg[scope][i]['filesize'] = filesize\\n\\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\\n    os.system('rm -r /home/pi/OpenScan/tmp/update')    \\nos.makedirs('/home/pi/OpenScan/tmp/update/')\\n\\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\\n    json.dump(msg, f, indent=4)\\n\\nfor i in msg[scope]:\\n    if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\\n        os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\\n    shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\\n\\nmsg['payload'] = 'created with scope: ' + scope\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 300,\n        \"y\": 820,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"80175eb8dc6ad009\",\n        \"type\": \"inject\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 140,\n        \"y\": 820,\n        \"wires\": [\n            [\n                \"4c7fa5b5b27b83a5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3f42560297fe6978\",\n        \"type\": \"ui_template\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"name\": \"Download LOG\",\n        \"order\": 9,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href='tmp/log.txt'>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download error log\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 140,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c94623ddd9d95f78\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"get update\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['status'] == \\\"No new update available\\\":\\n    save('updateable',False)\\nelif msg['status'] == \\\"New update available\\\":\\n    save('updateable',True)\\n\",\n        \"outputs\": 1,\n        \"x\": 170,\n        \"y\": 60,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"39a502b38837273d\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"1e7457ea9c2c5e09\"\n        ],\n        \"x\": 205,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"901e31453b2bdff8\",\n        \"type\": \"delay\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"10\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 180,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f983854748ee4763\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"{{msg.osdate}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 520,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5347c7c517f5e8c7\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU temp:\",\n        \"format\": \"{{msg.temp}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 470,\n        \"y\": 560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"3a5016f7003cd72c\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU memory:\",\n        \"format\": \"{{msg.cpu}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6d720c4a4ecd9475\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 6,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Swap memory:\",\n        \"format\": \"{{msg.swap}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 640,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6438b7d060a70d81\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 7,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Diskspace:\",\n        \"format\": \"{{msg.diskspace}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 470,\n        \"y\": 680,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d7362e6e0ec7bdaa\",\n        \"type\": \"inject\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"true\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"x\": 130,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"4ce127c61c3c5966\",\n                \"beacc3dc5398fa79\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4ce127c61c3c5966\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"prepare image creation\",\n        \"func\": \"import os\\n\\n#factory reset, reset wpa, create wpa in boot, rm files\\n#should be done before creating a new raspbian image\\n\\nbasepath = '/home/pi/OpenScan/'\\n\\n#remove files\\n\\ndir = basepath + 'scans/'\\n\\nfor i in ['scans/','tmp/']:\\n    os.system('rm -r ' + basepath + i)\\n    os.mkdir(basepath + i)\\n\\n#delete wifi\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('wpa_cli -i wlan0 reconfigure')\\n\\n#create new wpa_supplicant.conf\\nwith open('/boot/wpa_supplicant.conf','w+') as file:\\n    file.write('country=de\\\\nupdate_config=1\\\\nctrl_interface=/var/run/wpa_supplicant\\\\n\\\\nnetwork={\\\\n scan_ssid=1\\\\n ssid=\\\"wlan name\\\"\\\\n psk=\\\"xxxx\\\"\\\\n}')\\n\\n#rm tmp dir\\n\\n\\n#stop photos:\\nos.system('systemctl stop flask')\\nos.system('rm -r ' + basepath + 'tmp')\\nos.system('mkdir ' + basepath + 'tmp')\\n\\nos.system('systemctl stop nodered')\\n\\n#reset factory\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 330,\n        \"y\": 900,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"beacc3dc5398fa79\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 235,\n        \"y\": 940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8d012912f302be85\",\n        \"type\": \"ui_button\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 8,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Show Details/Changelog\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 170,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"5242607a723cc628\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5242607a723cc628\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"Changelog\",\n        \"func\": \"import requests\\n\\ntempfile = '/home/pi/OpenScan/tmp/changelog'\\n\\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\\nr = requests.get(url, allow_redirects=False)\\n\\nwith open(tempfile,'wb') as file:\\n    file.write(r.content)\\n    \\nwith open(tempfile, 'r') as file:\\n    text = file.read()\\n    \\ntext = text.replace('\\\\n','<br>').replace('*', '&emsp; - ')\\nmsg['payload'] = text\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 390,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"573722197b15bf84\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573722197b15bf84\",\n        \"type\": \"ui_toast\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 570,\n        \"y\": 340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c745f4a695e8ac45\",\n        \"type\": \"inject\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"main\",\n        \"payloadType\": \"str\",\n        \"x\": 370,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"1e10b387ee30c486\"\n            ]\n        ]\n    }\n]"
  },
  {
    "path": "update/beta/settings.js",
    "content": "/**\n * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT\n *\n * It can contain any valid JavaScript code that will get run when Node-RED\n * is started.\n *\n * Lines that start with // are commented out.\n * Each entry should be separated from the entries above and below by a comma ','\n *\n * For more information about individual settings, refer to the documentation:\n *    https://nodered.org/docs/user-guide/runtime/configuration\n *\n * The settings are split into the following sections:\n *  - Flow File and User Directory Settings\n *  - Security\n *  - Server Settings\n *  - Runtime Settings\n *  - Editor Settings\n *  - Node Settings\n *\n **/\n\nmodule.exports = {\n\n/*******************************************************************************\n * Flow File and User Directory Settings\n *  - flowFile\n *  - credentialSecret\n *  - flowFilePretty\n *  - userDir\n *  - nodesDir\n ******************************************************************************/\n\n    /** The file containing the flows. If not set, defaults to flows_<hostname>.json **/\n    flowFile: \"flows.json\",\n\n    /** By default, credentials are encrypted in storage using a generated key. To\n     * specify your own secret, set the following property.\n     * If you want to disable encryption of credentials, set this property to false.\n     * Note: once you set this property, do not change it - doing so will prevent\n     * node-red from being able to decrypt your existing credentials and they will be\n     * lost.\n     */\n    credentialSecret: false,\n\n    /** By default, the flow JSON will be formatted over multiple lines making\n     * it easier to compare changes when using version control.\n     * To disable pretty-printing of the JSON set the following property to false.\n     */\n    flowFilePretty: true,\n\n    /** By default, all user data is stored in a directory called `.node-red` under\n     * the user's home directory. To use a different location, the following\n     * property can be used\n     */\n    //userDir: '/home/nol/.node-red/',\n\tuserDir: '/home/pi/OpenScan/settings/.node-red/',\n    /** Node-RED scans the `nodes` directory in the userDir to find local node files.\n     * The following property can be used to specify an additional directory to scan.\n     */\n    //nodesDir: '/home/nol/.node-red/nodes',\n\n/*******************************************************************************\n * Security\n *  - adminAuth\n *  - https\n *  - httpsRefreshInterval\n *  - requireHttps\n *  - httpNodeAuth\n *  - httpStaticAuth\n ******************************************************************************/\n\n    /** To password protect the Node-RED editor and admin API, the following\n     * property can be used. See http://nodered.org/docs/security.html for details.\n     */\n    //adminAuth: {\n    //    type: \"credentials\",\n    //    users: [{\n    //        username: \"admin\",\n    //        password: \"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\",\n    //        permissions: \"*\"\n    //    }]\n    //},\n    \n    /** The following property can be used to enable HTTPS\n     * This property can be either an object, containing both a (private) key\n     * and a (public) certificate, or a function that returns such an object.\n     * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener\n     * for details of its contents.\n     */\n\n    /** Option 1: static object */\n    //https: {\n    //  key: require(\"fs\").readFileSync('privkey.pem'),\n    //  cert: require(\"fs\").readFileSync('cert.pem')\n    //},\n\n    /** Option 2: function that returns the HTTP configuration object */\n    // https: function() {\n    //     // This function should return the options object, or a Promise\n    //     // that resolves to the options object\n    //     return {\n    //         key: require(\"fs\").readFileSync('privkey.pem'),\n    //         cert: require(\"fs\").readFileSync('cert.pem')\n    //     }\n    // },\n\n    /** If the `https` setting is a function, the following setting can be used\n     * to set how often, in hours, the function will be called. That can be used\n     * to refresh any certificates.\n     */\n    //httpsRefreshInterval : 12,\n\n    /** The following property can be used to cause insecure HTTP connections to\n     * be redirected to HTTPS.\n     */\n    //requireHttps: true,\n\n    /** To password protect the node-defined HTTP endpoints (httpNodeRoot),\n     * including node-red-dashboard, or the static content (httpStatic), the\n     * following properties can be used.\n     * The `pass` field is a bcrypt hash of the password.\n     * See http://nodered.org/docs/security.html#generating-the-password-hash\n     */\n    //httpNodeAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n    //httpStaticAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n\n/*******************************************************************************\n * Server Settings\n *  - uiPort\n *  - uiHost\n *  - apiMaxLength\n *  - httpServerOptions\n *  - httpAdminRoot\n *  - httpAdminMiddleware\n *  - httpNodeRoot\n *  - httpNodeCors\n *  - httpNodeMiddleware\n *  - httpStatic\n ******************************************************************************/\n\n    /** the tcp port that the Node-RED web server is listening on */\n//    uiPort: process.env.PORT || 1880,\nuiPort: process.env.PORT || 80,\n    /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.\n     * To listen on all IPv6 addresses, set uiHost to \"::\",\n     * The following property can be used to listen on a specific interface. For\n     * example, the following would only allow connections from the local machine.\n     */\n    //uiHost: \"127.0.0.1\",\n\n    /** The maximum size of HTTP request that will be accepted by the runtime api.\n     * Default: 5mb\n     */\n    //apiMaxLength: '5mb',\n\n    /** The following property can be used to pass custom options to the Express.js\n     * server used by Node-RED. For a full list of available options, refer\n     * to http://expressjs.com/en/api.html#app.settings.table\n     */\n    //httpServerOptions: { },\n\n    /** By default, the Node-RED UI is available at http://localhost:1880/\n     * The following property can be used to specify a different root path.\n     * If set to false, this is disabled.\n     */\n    //httpAdminRoot: '/admin',\nhttpAdminRoot: '/editor',\n    /** The following property can be used to add a custom middleware function\n     * in front of all admin http routes. For example, to set custom http\n     * headers. It can be a single function or an array of middleware functions.\n     */\n    // httpAdminMiddleware: function(req,res,next) {\n    //    // Set the X-Frame-Options header to limit where the editor\n    //    // can be embedded\n    //    //res.set('X-Frame-Options', 'sameorigin');\n    //    next();\n    // },\n\n\n    /** Some nodes, such as HTTP In, can be used to listen for incoming http requests.\n     * By default, these are served relative to '/'. The following property\n     * can be used to specifiy a different root path. If set to false, this is\n     * disabled.\n     */\n    //httpNodeRoot: '/red-nodes',\n\n    /** The following property can be used to configure cross-origin resource sharing\n     * in the HTTP nodes.\n     * See https://github.com/troygoode/node-cors#configuration-options for\n     * details on its contents. The following is a basic permissive set of options:\n     */\n    //httpNodeCors: {\n    //    origin: \"*\",\n    //    methods: \"GET,PUT,POST,DELETE\"\n    //},\n\n    /** If you need to set an http proxy please set an environment variable\n     * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.\n     * For example - http_proxy=http://myproxy.com:8080\n     * (Setting it here will have no effect)\n     * You may also specify no_proxy (or NO_PROXY) to supply a comma separated\n     * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk\n     */\n\n    /** The following property can be used to add a custom middleware function\n     * in front of all http in nodes. This allows custom authentication to be\n     * applied to all http in nodes, or any other sort of common request processing.\n     * It can be a single function or an array of middleware functions.\n     */\n    //httpNodeMiddleware: function(req,res,next) {\n    //    // Handle/reject the request, or pass it on to the http in node by calling next();\n    //    // Optionally skip our rawBodyParser by setting this to true;\n    //    //req.skipRawBodyParser = true;\n    //    next();\n    //},\n\n    /** When httpAdminRoot is used to move the UI to a different root path, the\n     * following property can be used to identify a directory of static content\n     * that should be served at http://localhost:1880/.\n     */\n    //httpStatic: '/home/nol/node-red-static/',\nhttpStatic: '/home/pi/OpenScan/',\n/*******************************************************************************\n * Runtime Settings\n *  - lang\n *  - logging\n *  - contextStorage\n *  - exportGlobalContextKeys\n *  - externalModules\n ******************************************************************************/\n\n     /** Uncomment the following to run node-red in your preferred language.\n      * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko\n      * Some languages are more complete than others.\n      */\n     // lang: \"de\",\n\n     /** Configure the logging output */\n     logging: {\n         /** Only console logging is currently supported */\n         console: {\n             /** Level of logging to be recorded. Options are:\n              * fatal - only those errors which make the application unusable should be recorded\n              * error - record errors which are deemed fatal for a particular request + fatal errors\n              * warn - record problems which are non fatal + errors + fatal errors\n              * info - record information about the general running of the application + warn + error + fatal errors\n              * debug - record information which is more verbose than info + info + warn + error + fatal errors\n              * trace - record very detailed logging + debug + info + warn + error + fatal errors\n              * off - turn off all logging (doesn't affect metrics or audit)\n              */\n             level: \"info\",\n             /** Whether or not to include metric events in the log output */\n             metrics: false,\n             /** Whether or not to include audit events in the log output */\n             audit: false\n         }\n     },\n\n     /** Context Storage\n      * The following property can be used to enable context storage. The configuration\n      * provided here will enable file-based context that flushes to disk every 30 seconds.\n      * Refer to the documentation for further options: https://nodered.org/docs/api/context/\n      */\n     //contextStorage: {\n     //    default: {\n     //        module:\"localfilesystem\"\n     //    },\n     //},\n\n     /** `global.keys()` returns a list of all properties set in global context.\n      * This allows them to be displayed in the Context Sidebar within the editor.\n      * In some circumstances it is not desirable to expose them to the editor. The\n      * following property can be used to hide any property set in `functionGlobalContext`\n      * from being list by `global.keys()`.\n      * By default, the property is set to false to avoid accidental exposure of\n      * their values. Setting this to true will cause the keys to be listed.\n      */\n     exportGlobalContextKeys: false,\n\n     /** Configure how the runtime will handle external npm modules.\n      * This covers:\n      *  - whether the editor will allow new node modules to be installed\n      *  - whether nodes, such as the Function node are allowed to have their\n      * own dynamically configured dependencies.\n      * The allow/denyList options can be used to limit what modules the runtime\n      * will install/load. It can use '*' as a wildcard that matches anything.\n      */\n     externalModules: {\n         // autoInstall: false,   /** Whether the runtime will attempt to automatically install missing modules */\n         // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */\n         // palette: {              /** Configuration for the Palette Manager */\n         //     allowInstall: true, /** Enable the Palette Manager in the editor */\n         //     allowUpload: true,  /** Allow module tgz files to be uploaded and installed */\n         //     allowList: [],\n         //     denyList: []\n         // },\n         // modules: {              /** Configuration for node-specified modules */\n         //     allowInstall: true,\n         //     allowList: [],\n         //     denyList: []\n         // }\n     },\n\n\n/*******************************************************************************\n * Editor Settings\n *  - disableEditor\n *  - editorTheme\n ******************************************************************************/\n\n    /** The following property can be used to disable the editor. The admin API\n     * is not affected by this option. To disable both the editor and the admin\n     * API, use either the httpRoot or httpAdminRoot properties\n     */\n    //disableEditor: false,\n\n    /** Customising the editor\n     * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes\n     * for all available options.\n     */\n    editorTheme: {\n        /** The following property can be used to set a custom theme for the editor.\n         * See https://github.com/node-red-contrib-themes/theme-collection for\n         * a collection of themes to chose from.\n         */\n        //theme: \"\",\n        palette: {\n            /** The following property can be used to order the categories in the editor\n             * palette. If a node's category is not in the list, the category will get\n             * added to the end of the palette.\n             * If not set, the following default order is used:\n             */\n            //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],\n        },\n        projects: {\n            /** To enable the Projects feature, set this value to true */\n            enabled: false,\n            workflow: {\n                /** Set the default projects workflow mode.\n                 *  - manual - you must manually commit changes\n                 *  - auto - changes are automatically committed\n                 * This can be overridden per-user from the 'Git config'\n                 * section of 'User Settings' within the editor\n                 */\n                mode: \"manual\"\n            }\n        },\n        codeEditor: {\n            /** Select the text editor component used by the editor.\n             * Defaults to \"ace\", but can be set to \"ace\" or \"monaco\"\n             */\n            lib: \"ace\",\n            options: {\n                /** The follow options only apply if the editor is set to \"monaco\"\n                 *\n                 * theme - must match the file name of a theme in\n                 * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme\n                 * e.g. \"tomorrow-night\", \"upstream-sunburst\", \"github\", \"my-theme\"\n                 */\n                theme: \"vs\",\n                /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.\n                 * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html\n                 */\n                //fontSize: 14,\n                //fontFamily: \"Cascadia Code, Fira Code, Consolas, 'Courier New', monospace\",\n                //fontLigatures: true,\n            }\n        }\n    },\n\n/*******************************************************************************\n * Node Settings\n *  - fileWorkingDirectory\n *  - functionGlobalContext\n *  - functionExternalModules\n *  - nodeMessageBufferMaxLength\n *  - ui (for use with Node-RED Dashboard)\n *  - debugUseColors\n *  - debugMaxLength\n *  - execMaxBufferSize\n *  - httpRequestTimeout\n *  - mqttReconnectTime\n *  - serialReconnectTime\n *  - socketReconnectTime\n *  - socketTimeout\n *  - tcpMsgQueueSize\n *  - inboundWebSocketTimeout\n *  - tlsConfigDisableLocalFiles\n *  - webSocketNodeVerifyClient\n ******************************************************************************/\n\n    /** The working directory to handle relative file paths from within the File nodes\n     * defaults to the working directory of the Node-RED process.\n     */\n    //fileWorkingDirectory: \"\",\n\n    /** Allow the Function node to load additional npm modules directly */\n    functionExternalModules: true,\n\n    /** The following property can be used to set predefined values in Global Context.\n     * This allows extra node modules to be made available with in Function node.\n     * For example, the following:\n     *    functionGlobalContext: { os:require('os') }\n     * will allow the `os` module to be accessed in a Function node using:\n     *    global.get(\"os\")\n     */\n    functionGlobalContext: {\n        os:require('os'),\n        path:require('path'),\n        fs:require('fs'),\n\n},\n\n    /** The maximum number of messages nodes will buffer internally as part of their\n     * operation. This applies across a range of nodes that operate on message sequences.\n     * defaults to no limit. A value of 0 also means no limit is applied.\n     */\n    //nodeMessageBufferMaxLength: 0,\n\n    /** If you installed the optional node-red-dashboard you can set it's path\n     * relative to httpNodeRoot\n     * Other optional properties include\n     *  readOnly:{boolean},\n     *  middleware:{function or array}, (req,res,next) - http middleware\n     *  ioMiddleware:{function or array}, (socket,next) - socket.io middleware\n     */\n    //ui: { path: \"ui\" },\nui: { path: \"\" },\n    /** Colourise the console output of the debug node */\n    //debugUseColors: true,\n\n    /** The maximum length, in characters, of any message sent to the debug sidebar tab */\n    debugMaxLength: 1000,\n\n    /** Maximum buffer size for the exec node. Defaults to 10Mb */\n    //execMaxBufferSize: 10000000,\n\n    /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */\n    //httpRequestTimeout: 120000,\n\n    /** Retry time in milliseconds for MQTT connections */\n    mqttReconnectTime: 15000,\n\n    /** Retry time in milliseconds for Serial port connections */\n    serialReconnectTime: 15000,\n\n    /** Retry time in milliseconds for TCP socket connections */\n    //socketReconnectTime: 10000,\n\n    /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */\n    //socketTimeout: 120000,\n\n    /** Maximum number of messages to wait in queue while attempting to connect to TCP socket\n     * defaults to 1000\n     */\n    //tcpMsgQueueSize: 2000,\n\n    /** Timeout in milliseconds for inbound WebSocket connections that do not\n     * match any configured node. Defaults to 5000\n     */\n    //inboundWebSocketTimeout: 5000,\n\n    /** To disable the option for using local files for storing keys and\n     * certificates in the TLS configuration node, set this to true.\n     */\n    //tlsConfigDisableLocalFiles: true,\n\n    /** The following property can be used to verify websocket connection attempts.\n     * This allows, for example, the HTTP request headers to be checked to ensure\n     * they include valid authentication information.\n     */\n    //webSocketNodeVerifyClient: function(info) {\n    //    /** 'info' has three properties:\n    //    *   - origin : the value in the Origin header\n    //    *   - req : the HTTP request\n    //    *   - secure : true if req.connection.authorized or req.connection.encrypted is set\n    //    *\n    //    * The function should return true if the connection should be accepted, false otherwise.\n    //    *\n    //    * Alternatively, if this function is defined to accept a second argument, callback,\n    //    * it can be used to verify the client asynchronously.\n    //    * The callback takes three arguments:\n    //    *   - result : boolean, whether to accept the connection or not\n    //    *   - code : if result is false, the HTTP error status to return\n    //    *   - reason: if result is false, the HTTP reason string to return\n    //    */\n    //},\n}\n"
  },
  {
    "path": "update/betaArdu/Arducam.py",
    "content": "import time\nimport os\n\ntry:\n    import v4l2\nexcept Exception as e:\n    print(e)\n    print(\"Try to install v4l2-fix\")\n    try:\n        from pip import main as pipmain\n    except ImportError:\n        from pip._internal import main as pipmain\n    pipmain(['install', 'v4l2-fix'])\n    print(\"\\nTry to run the focus program again.\")\n    exit(0)\n\nimport fcntl\nimport errno\n\n# # Type\n# v4l2.V4L2_CTRL_TYPE_INTEGER\n# v4l2.V4L2_CTRL_TYPE_BOOLEAN\n# v4l2.V4L2_CTRL_TYPE_MENU\n# v4l2.V4L2_CTRL_TYPE_BUTTON\n# v4l2.V4L2_CTRL_TYPE_INTEGER64\n# v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n# # Flags\n# v4l2.V4L2_CTRL_FLAG_DISABLED\n# v4l2.V4L2_CTRL_FLAG_GRABBED\n# v4l2.V4L2_CTRL_FLAG_READ_ONLY\n# v4l2.V4L2_CTRL_FLAG_UPDATE\n# v4l2.V4L2_CTRL_FLAG_INACTIVE\n# v4l2.V4L2_CTRL_FLAG_SLIDER\n\ndef assert_valid_queryctrl(queryctrl):\n    return queryctrl.type & (\n        v4l2.V4L2_CTRL_TYPE_INTEGER\n        | v4l2.V4L2_CTRL_TYPE_BOOLEAN\n        | v4l2.V4L2_CTRL_TYPE_MENU\n        | v4l2.V4L2_CTRL_TYPE_BUTTON\n        | v4l2.V4L2_CTRL_TYPE_INTEGER64\n        | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n        | 7\n        | 8\n        | 9\n    ) and queryctrl.flags & (\n        v4l2.V4L2_CTRL_FLAG_DISABLED\n        | v4l2.V4L2_CTRL_FLAG_GRABBED\n        | v4l2.V4L2_CTRL_FLAG_READ_ONLY\n        | v4l2.V4L2_CTRL_FLAG_UPDATE\n        | v4l2.V4L2_CTRL_FLAG_INACTIVE\n        | v4l2.V4L2_CTRL_FLAG_SLIDER\n    )\n\ndef get_device_controls_menu(fd, queryctrl):\n    querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum)\n    while querymenu.index <= queryctrl.maximum:\n        fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu)\n        yield querymenu\n        querymenu.index += 1\n\ndef get_device_controls_by_class(fd, control_class):\n    # enumeration by control class\n    queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n    while True:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n        except IOError as e:\n            assert e.errno == errno.EINVAL\n            break\n        if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class:\n            break\n        yield queryctrl\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n\ndef getdict(struct):\n    val = dict((field, getattr(struct, field)) for field, _ in struct._fields_)\n    val.pop(\"reserved\")\n    return val\n\ndef get_device_controls(fd):\n    # original enumeration method\n    queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE)\n    while queryctrl.id < v4l2.V4L2_CID_LASTP1:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n            print(queryctrl.name)\n        except IOError as e:\n            # this predefined control is not supported by this device\n            assert e.errno == errno.EINVAL\n            queryctrl.id += 1\n            continue\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1)\n\ndef get_ctrls(vd):\n    ctrls = []\n    # enumeration by control class\n    for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA):\n        for queryctrl in get_device_controls_by_class(vd, class_):\n            ctrl = getdict(queryctrl)\n            if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    # print(querymenu.name)\n                    ctrl[\"menu\"].append(querymenu.name)\n\n            if queryctrl.type == 9:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    ctrl[\"menu\"].append(querymenu.index)\n            ctrls.append(ctrl)\n    return ctrls\n\ndef set_ctrl(vd, id, value):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    ctrl.value = value\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n\ndef get_ctrl(vd, id):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n        return None\n    return ctrl.value\n\n\nclass Focuser:\n    FOCUS_ID = 0x009a090a\n    dev = None\n\n    def __init__(self, dev=0):\n        self.focus_value = 0\n        self.dev = dev\n\n        if type(dev) == int or (type(dev) == str and dev.isnumeric()):\n            self.dev = \"/dev/video{}\".format(dev)\n\n        self.fd = open(self.dev, 'r')\n        self.ctrls = get_ctrls(self.fd)\n        self.hasFocus = False\n        for ctrl in self.ctrls:\n            if ctrl['id'] == Focuser.FOCUS_ID:\n                self.hasFocus = True\n                self.opts[Focuser.OPT_FOCUS][\"MIN_VALUE\"] = ctrl['minimum']\n                self.opts[Focuser.OPT_FOCUS][\"MAX_VALUE\"] = ctrl['maximum']\n                self.opts[Focuser.OPT_FOCUS][\"DEF_VALUE\"] = ctrl['default']\n                self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID)\n\n        if not self.hasFocus:\n            raise RuntimeError(\"Device {} has no focus_absolute control.\".format(self.dev))\n\n    def read(self):\n        return self.focus_value\n\n    def write(self, value):\n        self.focus_value = value\n        # os.system(\"v4l2-ctl -d {} -c focus_absolute={}\".format(self.dev, value))\n        set_ctrl(self.fd, Focuser.FOCUS_ID, value)\n\n    OPT_BASE    = 0x1000\n    OPT_FOCUS   = OPT_BASE | 0x01\n    OPT_ZOOM    = OPT_BASE | 0x02\n    OPT_MOTOR_X = OPT_BASE | 0x03\n    OPT_MOTOR_Y = OPT_BASE | 0x04\n    OPT_IRCUT   = OPT_BASE | 0x05\n    opts = {\n        OPT_FOCUS : {\n            \"MIN_VALUE\": 0,\n            \"MAX_VALUE\": 1000,\n            \"DEF_VALUE\": 0,\n        },\n    }\n    def reset(self,opt,flag = 1):\n        info = self.opts[opt]\n        if info == None or info[\"DEF_VALUE\"] == None:\n            return\n        self.set(opt,info[\"DEF_VALUE\"])\n\n    def get(self,opt,flag = 0):\n        info = self.opts[opt]\n        return self.read()\n\n    def set(self,opt,value,flag = 1):\n        info = self.opts[opt]\n        if value > info[\"MAX_VALUE\"]:\n            value = info[\"MAX_VALUE\"]\n        elif value < info[\"MIN_VALUE\"]:\n            value = info[\"MIN_VALUE\"]\n        self.write(value)\n        print(\"write: {}\".format(value))\n\n    def __del__(self):\n        self.fd.close()\n\npass\n"
  },
  {
    "path": "update/betaArdu/OpenScan.py",
    "content": "basepath = '/home/pi/OpenScan/'\nfrom os.path import isfile\nimport os\n\ndef load_bool(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    if value == '1' or value == 'True' or value =='true':\n        value = True\n    else:\n        value = False\n    return value\n\ndef fade_led(pin_led, fade_steps, duty_max, dir = True):\n    import RPi.GPIO as GPIO\n    import time\n    GPIO.setmode(GPIO.BCM)\n    GPIO.setwarnings(False)\n    GPIO.setup(pin_led, GPIO.OUT)\n    pwm = GPIO.PWM(pin_led, 200)\n\n    if dir:\n        pwm.start(0)\n        for duty_cycle in range(0, fade_steps*10, 1):  # Increase duty cycle in steps\n            pwm.ChangeDutyCycle(duty_max*duty_cycle/(10*fade_steps))\n            time.sleep(0.001)  # Pause between steps (adjust as needed)\n    else:\n        pwm.start(duty_max)\n        for duty_cycle in range(fade_steps*10,0, -1):  # Increase duty cycle in steps\n            pwm.ChangeDutyCycle(duty_max*duty_cycle/(10*fade_steps))\n            time.sleep(0.001)  # Pause between steps (adjust as needed)\n    pwm.stop()\n\n\ndef check_hotspot_mode(interface=\"wlan0\"):\n    import subprocess\n    try:\n        output = subprocess.check_output([\"iwconfig\", interface]).decode(\"utf-8\")\n        if \"Mode:Master\" in output:\n            return True\n        elif \"Mode:Managed\" in output:\n            return False\n        else:\n            return False\n    except subprocess.CalledProcessError as e:\n        return False\n\n\n\ndef add_wifi_network(ssid, password, country):\n    import re\n    conf_file = \"/etc/wpa_supplicant/wpa_supplicant-wlan0.conf\"\n\n    if not os.path.exists(conf_file):\n        return False\n\n    if not (ssid and password and country):\n        return False\n\n    with open(conf_file, \"r\") as f:\n        content = f.read()\n\n    updated_content = re.sub(r'country=\\w+', f'country={country}', content)\n\n    if f'ssid=\"{ssid}\"' in content:\n        network_block_pattern = re.compile(\n            r'network=\\{\\s*ssid=\"' + re.escape(ssid) + r'\".*?psk=\".*?\".*?\\}', re.DOTALL\n        )\n        updated_network_block = f'network={{\\n    ssid=\"{ssid}\"\\n    psk=\"{password}\"\\n    key_mgmt=WPA-PSK\\n}}'\n        updated_content = network_block_pattern.sub(updated_network_block, updated_content)\n    else:\n        network_block = f'\\nnetwork={{\\n    ssid=\"{ssid}\"\\n    psk=\"{password}\"\\n    key_mgmt=WPA-PSK\\n}}\\n'\n        updated_content += network_block\n\n    with open(conf_file, \"w\") as f:\n        f.write(updated_content)\n    os.system(\"sudo systemctl restart wpa_supplicant@wlan0\")\n\n    return True\n\n\ndef load_str(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    return value\n\ndef load_int(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = int(file.read().replace('\\n',''))\n    return value\n\ndef load_float(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = float(file.read().replace('\\n',''))\n    return value\n\ndef save(name, value):\n    filename = basepath+'settings/'+name\n    with open(filename, 'w+') as file:\n        file.write(str(value))\n    return\n\ndef OpenScanCloud(cmd, msg):\n    from requests import get\n    osc_user = 'openscan'\n    osc_pw = 'free'\n    osc_server = 'http://openscanfeedback.dnsuser.de:1334/'\n\n    try:\n        r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg)\n    except:\n        r = type('obj', (object,), {'status_code' : 404, 'text':None})\n    return r\n\ndef camera(cmd, msg = {}):\n    from requests import get\n    flask = 'http://127.0.0.1:1312/'\n    try:\n        r = get(flask + cmd, params=msg)\n        return r.status_code\n    except:\n        return 400\n\ndef motorrun(motor,angle,ES_enable=False,ES_start_state = True):\n    #motor can be \"rotor\", \"tt\" or \"extra\"\n    import RPi.GPIO as GPIO\n    from time import sleep\n    from math import cos\n    msg = {'cmd':'set'}\n\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n\n    spr = load_int(motor + '_stepsperrotation')\n    dirpin = load_int('pin_' + motor + '_dir')\n    steppin = load_int('pin_' + motor +'_step')\n    ES_pin = load_int('pin_' + motor + '_endstop')\n    dir = load_int(motor + '_dir')\n    ramp = load_int(motor + '_accramp')\n    acc = load_float(motor + '_acc')\n    delay_init = load_float(motor + '_delay')\n    delay = delay_init\n\n    step_count=int(angle*spr/360) * dir\n    GPIO.setup(dirpin, GPIO.OUT)\n    GPIO.setup(steppin, GPIO.OUT)\n    GPIO.setup(ES_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP)\n\n    if (step_count>0):\n        GPIO.output(dirpin, GPIO.HIGH)\n    if(step_count<0):\n        GPIO.output(dirpin, GPIO.LOW)\n        step_count=-step_count\n    for x in range(step_count):\n        if ES_enable == True and GPIO.input(ES_pin) != ES_start_state:\n            i = 0\n            while i <= 10:\n                if GPIO.input(ES_pin) == ES_start_state:\n                    i = 11\n                if i == 10:\n                    return\n                i = i + 1\n\n        GPIO.output(steppin, GPIO.HIGH)\n        if x<=ramp and x<=step_count/2:\n            delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc)\n            #delay=delay_init+(ramp-x)*(delay_init)/acc\n        elif step_count-x<=ramp and x>step_count/2:\n            delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc)\n            #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc\n        else:\n            delay = delay_init\n        sleep(delay)\n        GPIO.output(steppin, GPIO.LOW)\n        sleep(delay)\n\ndef ringlight(number,state):\n    import RPi.GPIO as GPIO\n    msg = {'cmd':'set'}\n    pin = load_int('pin_ringlight' + str(number))\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, state)\n\ndef take_photo(file):\n    from os import system\n    filepath = basepath + file\n\n    model=load_str('model')\n\n\n\n    shutter = str(load_int('cam_shutter'))\n    saturation = load_str('cam_saturation')\n    contrast = load_str('cam_contrast')\n    awbg_red = load_str('cam_awbg_red')\n    awbg_blue = load_str('cam_awbg_blue')\n    gain = load_str('cam_gain')\n    quality = load_int('cam_jpeg_quality')\n    filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg'\n    #width = load_str('cam_resx')\n    #height = load_str('cam_resy')\n    timeout = load_str('cam_timeout')\n    cropx = load_int('cam_cropx')/200\n    cropy = load_int('cam_cropy')/200\n    rotation = load_int('cam_rotation')\n    AF = load_bool('cam_AFmode')\n    camera = load_str('camera')\n\n\n    if camera == 'imx519' and AF == True:\n        autofocus = ' --autofocus '\n    else:\n        autofocus = ''\n\n    if camera  == \"usb_webcam\":\n        cmd = 'fswebcam -i 0 -r \"1280x720\" -F 5 --no-banner --jpeg 95 --save ' + filepath2\n    else:\n        cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1'\n    #    cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus\n        \n    system(cmd)\n    return cmd\n\ndef get_points(samples=1):\n    from math import pi, sqrt, acos, atan2, cos, sin\n\n    points = []\n    phi = pi * (3. - sqrt(5.))\n    for i in range(int(samples)):\n        y = 1 - (i / float(samples - 1)) * 2\n        radius = sqrt(1 - y * y)\n        theta = phi * i\n        x = cos(theta) * radius\n        z = sin(theta) * radius\n        r=sqrt(x*x+y*y+z*z)\n        theta_neu=acos(z/r)*180/pi\n        phi_neu=atan2(y,x)*180/pi\n        points.append((theta_neu-90,phi_neu))\n    points.sort()\n    return points\n\ndef create_coordinates(angle_min, angle_max,point_count):\n    point_count_final=point_count\n    if angle_max < angle_min:\n        a = angle_min\n        angle_min = angle_max\n        angle_max = a\n    point_count=point_count*90/(angle_max-angle_min)\n    actual_points=0\n    while actual_points<point_count_final:\n        points=get_points(point_count)\n        filtered=[]\n        for x,y in points:\n            if x>angle_min and x<angle_max and len(filtered)<point_count_final:\n                filtered.append((x,y))\n        actual_points=len(filtered)\n\n        if point_count-actual_points>20:\n            point_count=point_count+3\n        else:\n            point_count=point_count+1\n    return filtered\n\n\ndef haversine_distance_deg(theta1, phi1, theta2, phi2):\n    import numpy as np\n    R = 1\n    dtheta = np.radians(theta2 - theta1)\n    dphi = np.radians(phi2 - phi1)\n\n    theta1, phi1 = np.radians(theta1), np.radians(phi1)\n    theta2, phi2 = np.radians(theta2), np.radians(phi2)\n\n    a = np.sin(dtheta / 2) ** 2 + np.cos(theta1) * np.cos(theta2) * np.sin(dphi / 2) ** 2\n    c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))\n\n    return R * c\n\ndef sort_spherical_coordinates_deg(points_spherical_deg):\n    import numpy as np\n    from tsp_solver.greedy import solve_tsp\n\n    points_spherical_deg = np.array(points_spherical_deg)  # Convert list of tuples to NumPy array\n\n    n = len(points_spherical_deg)\n    dist_matrix = np.zeros((n, n))\n\n    # Calculate haversine distance for each pair of points\n    for i in range(n):\n        for j in range(i + 1, n):\n            dist = haversine_distance_deg(points_spherical_deg[i, 0], points_spherical_deg[i, 1],\n                                          points_spherical_deg[j, 0], points_spherical_deg[j, 1])\n            dist_matrix[i, j] = dist\n            dist_matrix[j, i] = dist\n\n    # Solve the TSP problem using the tsp_solver.greedy algorithm\n    path = solve_tsp(dist_matrix)\n\n    sorted_points_spherical_deg = points_spherical_deg[path]\n\n    # Convert the sorted NumPy array back to a list of tuples\n    return [tuple(point) for point in sorted_points_spherical_deg]\n"
  },
  {
    "path": "update/betaArdu/config.txt",
    "content": "# For more options and information see\n# http://rpf.io/configtxt\n# Some settings may impact device functionality. See link above for details\n\n# uncomment if you get no picture on HDMI for a default \"safe\" mode\n#hdmi_safe=1\n\n# uncomment the following to adjust overscan. Use positive numbers if console\n# goes off screen, and negative if there is too much border\n#overscan_left=16\n#overscan_right=16\n#overscan_top=16\n#overscan_bottom=16\n\n# uncomment to force a console size. By default it will be display's size minus\n# overscan.\n#framebuffer_width=1280\n#framebuffer_height=720\n\n# uncomment if hdmi display is not detected and composite is being output\n#hdmi_force_hotplug=1\n\n# uncomment to force a specific HDMI mode (this will force VGA)\n#hdmi_group=1\n#hdmi_mode=1\n\n# uncomment to force a HDMI mode rather than DVI. This can make audio work in\n# DMT (computer monitor) modes\n#hdmi_drive=2\n\n# uncomment to increase signal to HDMI, if you have interference, blanking, or\n# no display\n#config_hdmi_boost=4\n\n# uncomment for composite PAL\n#sdtv_mode=2\n\n#uncomment to overclock the arm. 700 MHz is the default.\n#arm_freq=800\n\n# Uncomment some or all of these to enable the optional hardware interfaces\n#dtparam=i2c_arm=on\n#dtparam=i2s=on\n#dtparam=spi=on\n\n# Uncomment this to enable infrared communication.\n#dtoverlay=gpio-ir,gpio_pin=17\n#dtoverlay=gpio-ir-tx,gpio_pin=18\n\n# Additional overlays and parameters are documented /boot/overlays/README\n\n# Enable audio (loads snd_bcm2835)\ndtparam=audio=on\n\n# Automatically load overlays for detected cameras\ncamera_auto_detect=1\n\n# Automatically load overlays for detected DSI displays\ndisplay_auto_detect=1\n\n# Enable DRM VC4 V3D driver\ndtoverlay=vc4-kms-v3d\nmax_framebuffers=2\n\n# Disable compensation for displays with overscan\ndisable_overscan=1\n\n[cm4]\n# Enable host mode on the 2711 built-in XHCI USB controller.\n# This line should be removed if the legacy DWC2 controller is required\n# (e.g. for USB device mode) or if USB support is not required.\notg_mode=1\n\n[all]\n\n[pi4]\n# Run as fast as firmware / board allows\narm_boost=1\n\n[all]\ncamera_auto_detect=0\ngpu_mem=256\ndtoverlay=vc4-fkms-v3d\ndtoverlay=imx519\n#dtoverlay=imx519,media-controller=1\n"
  },
  {
    "path": "update/betaArdu/fla.py",
    "content": "from flask import Flask, make_response, jsonify, request, abort\nfrom picamera2 import Picamera2\nfrom PIL import Image, ImageDraw, ImageOps, ImageFilter, ImageEnhance, ImageChops, ImageFont\nfrom time import sleep, time\nimport shutil\nfrom OpenScan import load_int, load_float, load_bool, ringlight\nimport RPi.GPIO as GPIO\nfrom math import sqrt\nimport os\nimport math\nfrom skimage import io, feature, color, transform\nimport numpy as np\nfrom scipy import ndimage\n\nGPIO.setwarnings(False)\nGPIO.setmode(GPIO.BCM)\n\napp = Flask(__name__)\n\nbasedir = '/home/pi/OpenScan/'\ntimer = time()\ncam_mode = 0\n\ndef overlay_mask(image, mask_image):\n    # Ensure image is in RGB mode\n    image_rgb = image.convert('RGB')\n    # Create an empty image with RGBA channels\n    overlay = Image.new('RGBA', image_rgb.size)\n\n    # Prepare a red image of the same size\n    red_image = Image.new('RGB', image_rgb.size, (255, 0, 0))\n    # Prepare a mask where the condition is met (mask_image pixels == 255)\n    mask_condition = np.array(mask_image) > 0\n    overlay_mask = Image.fromarray(np.uint8(mask_condition) * 255)\n    # Paste the red image onto the overlay using the condition mask\n    overlay.paste(red_image, mask=overlay_mask)\n    # Combine the original image with the overlay\n    combined = Image.alpha_composite(image_rgb.convert('RGBA'), overlay)\n    # Convert the final image to RGB\n    combined_rgb = combined.convert('RGB')\n    return combined_rgb\n\n\n\ndef highlight_sharpest_areas(image, threshold=load_int('cam_sharpness'), dilation_size=5):\n    # Convert PIL image to grayscale\n    image_gray = image.convert('L')\n\n    # Convert grayscale image to numpy array\n    image_array = np.array(image_gray)\n\n    # Calculate the gradient using a Sobel filter\n    dx = ndimage.sobel(image_array, 0)  # horizontal derivative\n    dy = ndimage.sobel(image_array, 1)  # vertical derivative\n    mag = np.hypot(dx, dy)  # magnitude\n\n    # Threshold the gradient to create a mask of the sharpest areas\n    mask = np.where(mag > threshold, 255, 0).astype(np.uint8)\n\n    dilated_mask = ndimage.binary_dilation(mask, structure=np.ones((dilation_size,dilation_size)))\n    # Create a PIL image from the mask\n    mask_image = Image.fromarray(dilated_mask)\n\n    return mask_image\n\n\n\n\n###################################################################################################################\n@app.route('/shutdown', methods=['get'])\ndef shutdown():\n    delay = 0.1\n    ringlight(2,False)\n\n    for i in range (5):\n        ringlight(1,True)\n        sleep(delay)\n        ringlight(1,False)\n        sleep(delay)\n    os.system('shutdown -h now')\n###################################################################################################################\n@app.route('/reboot', methods=['get'])\ndef reboot():\n    delay = 0.1\n    ringlight(2,False)\n\n    for i in range (5):\n        ringlight(1,True)\n        sleep(delay)\n        ringlight(1,False)\n        sleep(delay)\n\n    os.system('reboot -h')\n###################################################################################################################\n\ndef plot_orb_keypoints(pil_image):\n    downscale = 2\n    # Read the image from the given image path\n    image = np.array(pil_image)\n    #image = io.imread(image_path)\n    image = transform.resize(image, (image.shape[0] // downscale, image.shape[1] // downscale), anti_aliasing=True)\n\n    # Convert the image to grayscale\n    gray_image = color.rgb2gray(image)\n\n    try:\n        orb = feature.ORB(n_keypoints=10000, downscale=1.2, fast_n=2, fast_threshold=0.2 , n_scales=3, harris_k=0.001)\n        orb.detect_and_extract(gray_image)\n        keypoints = orb.keypoints\n    except:\n        return pil_image\n\n    # Convert the image back to the range [0, 255]\n    display_image = (image * 255).astype(np.uint8)\n\n    # Draw the keypoints on the image\n    draw = ImageDraw.Draw(pil_image)\n    size = max(2,int(image.shape[0]*downscale*0.005))\n    for i, (y, x) in enumerate(keypoints):\n        draw.ellipse([(downscale*x-size, downscale*y-size), (downscale*x+size, downscale*y+size)], fill = (0,255,0))\n    # Save the image with keypoints to the given output path\n    return pil_image\n\ndef add_histo(img):\n    histo_size = 241\n\n    img_gray = ImageOps.grayscale(img)\n    histogram = img_gray.histogram()\n    histogram_log = [math.log10(h + 1) for h in histogram]\n    histogram_max = max(histogram_log)\n    histogram_normalized = [float(h) / histogram_max for h in histogram_log]\n    hist_image = Image.new(\"RGBA\", (histo_size, histo_size), (255, 255, 255, 0))\n    draw = ImageDraw.Draw(hist_image)\n\n    for i in range(0, 256):\n        x = i\n        y = 256 - int(histogram_normalized[i] * 256)\n        draw.line((x, 256, x, y), fill=(0, 0, 0, 255))\n\n    text = \"\"\n    if min(histogram[235:238])>0:\n        text = \"overexposed\"\n    if sum(histogram[190:192])<8:\n        text = \"underexposed\"\n    font = ImageFont.truetype(\"DejaVuSans.ttf\", 30)\n\n    bbox = draw.textbbox((0, 0), text, font=font)\n\n    text_width = bbox[2] - bbox[0]\n    text_height = bbox[3] - bbox[1]\n\n\n    x = (hist_image.width - text_width )/2\n    y = hist_image.height - text_height - 10\n    draw.text((x, y), text, font=font, fill=(255,0,0))\n\n    scale = 0.25\n    width1, height1 = hist_image.size\n    width2 = img.size[0]\n    new_width1 = int(width2 * scale)\n    new_height1 = int((height1 / width1) * new_width1)\n    hist_image = hist_image.convert('RGB')\n\n    hist_image = hist_image.resize((new_width1, new_height1))\n    x = hist_image.width - text_width - 10\n    y = hist_image.height - text_height - 10\n\n\n    img.paste(hist_image, (img.size[0]-new_width1-int(0.01*img.size[0]),img.size[1]-new_height1-int(0.01*img.size[0])))\n\n    return img\n\ndef create_mask(image: Image, scale: float = 0.1, threshold: int = 45) -> Image:\n    threshold = load_int(\"cam_mask_threshold\")\n    if threshold <= 1:\n        return image\n    orig = image\n    image = image.resize((int(image.width*scale),int(image.height*scale)))\n    image = image.convert(\"L\")\n    reduced = image\n    image = image.filter(ImageFilter.EDGE_ENHANCE)\n    image = image.filter(ImageFilter.BLUR)\n    reduced = reduced.filter(ImageFilter.EDGE_ENHANCE_MORE)\n    mask = ImageChops.difference(image, reduced)\n    mask = ImageEnhance.Brightness(mask).enhance(2.5)\n    mask = mask.filter(ImageFilter.MaxFilter(9))\n    mask = mask.filter(ImageFilter.MinFilter(5))\n    mask = mask.point(lambda x: 255 if x <threshold else 0)\n    mask = mask.filter(ImageFilter.MaxFilter(5))\n    mask = mask.convert(orig.mode)\n    mask = mask.resize((orig.width,orig.height), resample=Image.BOX)\n    result = ImageChops.subtract(orig, mask)\n\n    return result\n\n###################################################################################################################\n@app.route('/picam2_init', methods=['get'])\ndef picam2_init():\n    global picam2\n    global preview_config\n    global capture_config\n\n    try:\n        picam2.controls.AnalogueGain = 1.0\n        return ({}, 200)\n    except:\n        pass\n    picam2 = Picamera2()\n\n#    preview_config = picam2.create_preview_configuration(main={\"size\": (1280, 720)}) #--> wrong aspect ratio!\n#    preview_config = picam2.create_preview_configuration(main={\"size\": (2028, 1520)})\n    preview_config = picam2.create_preview_configuration(main={\"size\": (2028, 1520)}, controls ={\"FrameDurationLimits\": (1, 1000000)})\n\n#    preview_config = picam2.create_preview_configuration(main={\"size\": (2328, 1748)})\n    capture_config = picam2.create_still_configuration(controls ={\"FrameDurationLimits\": (1, 1000000)})\n    picam2.configure(preview_config)\n    picam2.controls.AnalogueGain = 1.0\n    picam2.start()\n    return ({}, 200)\n\n###################################################################################################################\n@app.route('/picam2_take_photo', methods=['get'])\ndef picam2_take_photo():\n    starttime = time()\n\n    cropx = load_int('cam_cropx')/200\n    cropy = load_int('cam_cropy')/200\n    rotation = load_int('cam_rotation')\n    img = picam2.capture_image()\n\n    if cam_mode !=1:\n        img = img.convert('RGB')\n    w,h = img.size\n\n    if cropx != 0 or cropy != 0:\n        img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy)))\n\n    if rotation == 90:\n        img  = img.transpose(Image.ROTATE_90)\n    elif rotation == 180:\n        img= img.transpose(Image.ROTATE_180)\n    elif rotation == 270:\n        img= img.transpose(Image.ROTATE_270)\n\n    if load_bool(\"cam_mask\"):\n        if cam_mode == 1:\n            downscale = 0.045*1.4\n        else:\n            downscale = 0.1*1.4\n        img = create_mask(img, downscale)\n\n    if load_bool(\"cam_features\") and not load_bool(\"cam_sharparea\"):\n        img = plot_orb_keypoints(img)\n\n    if load_bool(\"cam_sharparea\") and not load_bool(\"cam_features\"):\n        img2 = highlight_sharpest_areas(img)\n        img = overlay_mask(img, img2)\n\n    if cam_mode != 1 and not  load_bool(\"cam_sharparea\") and not load_bool(\"cam_features\"):\n        img = add_histo(img)\n\n    img.save(\"/home/pi/OpenScan/tmp2/preview.jpg\", quality=load_int('cam_jpeg_quality'))\n    print(\"total \" + str(int(1000*(time()-starttime))) + \"ms\")\n    starttime = time()\n\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_focus', methods=['get'])\ndef picam2_focus():\n    focus = float(request.args.get('focus'))\n    picam2.set_controls({\"AfMode\": 0, \"LensPosition\": focus})\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_af1', methods=['get'])\ndef picam2_af1():\n    from libcamera import controls\n\n    picam2.set_controls({\"AfMode\": 2 ,\"AfTrigger\": 0, \"AfRange\":controls.AfRangeEnum.Macro})\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_af2', methods=['get'])\ndef picam2_af2():\n    picam2.set_controls({\"AfMode\": 2 ,\"AfTrigger\": 0})\n    return ({}, 200)\n\n###################################################################################################################\n@app.route('/picam2_exposure', methods=['get'])\ndef picam2_exposure():\n    exposure = int(request.args.get('exposure'))\n    picam2.controls.AnalogueGain = 1.0\n    picam2.controls.ExposureTime = exposure\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_contrast', methods=['get'])\ndef picam2_contrast():\n    contrast = float(request.args.get('contrast'))\n    picam2.controls.Contrast = contrast\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_saturation', methods=['get'])\ndef picam2_saturation():\n    saturation = float(request.args.get('saturation'))\n    picam2.controls.Saturation = saturation\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_switch_mode', methods=['get'])\ndef picam2_switch_mode():\n    global cam_mode\n    cam_mode = int(request.args.get('mode'))\n    if cam_mode == 1:\n        picam2.switch_mode(capture_config)\n    else:\n        picam2.switch_mode(preview_config)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/picam2_show_mode', methods=['get'])\ndef picam2_show_mode():\n    global cam_mode\n    return({\"mode\":cam_mode},200)\n###################################################################################################################\n@app.route('/picam2_af', methods=['get'])\ndef picam2_af():\n    picam2.set_controls({\"AfMode\": 1 ,\"AfTrigger\": 0}) # --> wait 3-5s\n    return ({}, 200)\n\nif __name__ == '__main__':\n#    app.run(host='127.0.0.1', port=1312, debug=False, threaded=True)\n    app.run(host='0.0.0.0', port=1312, debug=False, threaded=True)\n"
  },
  {
    "path": "update/betaArdu/flows.json",
    "content": "[\n    {\n        \"id\": \"e6f4d02efb300ea9\",\n        \"type\": \"tab\",\n        \"label\": \"Init\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"481edaf6db5a7a54\",\n        \"type\": \"tab\",\n        \"label\": \"Scan\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"80a3942785a26c29\",\n        \"type\": \"tab\",\n        \"label\": \"Files\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"e43a27722b508115\",\n        \"type\": \"tab\",\n        \"label\": \"Settings\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"a5557543ccff5889\",\n        \"type\": \"tab\",\n        \"label\": \"Update\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"90223f7ddc082321\",\n        \"type\": \"ui_group\",\n        \"name\": \"preview\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"7\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"e23b837a9f040895\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Scan\",\n        \"icon\": \"dashboard\",\n        \"order\": 2,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"5c06cb6bcc371ee6\",\n        \"type\": \"ui_base\",\n        \"theme\": {\n            \"name\": \"theme-dark\",\n            \"lightTheme\": {\n                \"default\": \"#0094CE\",\n                \"baseColor\": \"#0094CE\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"darkTheme\": {\n                \"default\": \"#097479\",\n                \"baseColor\": \"#097479\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"customTheme\": {\n                \"name\": \"Untitled Theme 1\",\n                \"default\": \"#4B7930\",\n                \"baseColor\": \"#4B7930\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"reset\": false\n            },\n            \"themeState\": {\n                \"base-color\": {\n                    \"default\": \"#097479\",\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-titlebar-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-backgroundColor\": {\n                    \"value\": \"#111111\",\n                    \"edited\": false\n                },\n                \"page-sidebar-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"group-textColor\": {\n                    \"value\": \"#0eb8c0\",\n                    \"edited\": false\n                },\n                \"group-borderColor\": {\n                    \"value\": \"#555555\",\n                    \"edited\": false\n                },\n                \"group-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"widget-textColor\": {\n                    \"value\": \"#eeeeee\",\n                    \"edited\": false\n                },\n                \"widget-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"widget-borderColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"base-font\": {\n                    \"value\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n                }\n            },\n            \"angularTheme\": {\n                \"primary\": \"indigo\",\n                \"accents\": \"blue\",\n                \"warn\": \"red\",\n                \"background\": \"grey\",\n                \"palette\": \"light\"\n            }\n        },\n        \"site\": {\n            \"name\": \"OpenScan\",\n            \"hideToolbar\": \"false\",\n            \"allowSwipe\": \"false\",\n            \"lockMenu\": \"false\",\n            \"allowTempTheme\": \"true\",\n            \"dateFormat\": \"DD/MM/YYYY\",\n            \"sizes\": {\n                \"sx\": 48,\n                \"sy\": 48,\n                \"gx\": 6,\n                \"gy\": 6,\n                \"cx\": 6,\n                \"cy\": 6,\n                \"px\": 0,\n                \"py\": 0\n            }\n        }\n    },\n    {\n        \"id\": \"34bc0fd2b0f2416c\",\n        \"type\": \"ui_link\",\n        \"name\": \"GitHub\",\n        \"link\": \"https://openscan-org.github.io/OpenScan-Doc/\",\n        \"icon\": \"fa-bookmark\",\n        \"target\": \"iframe\",\n        \"order\": 6\n    },\n    {\n        \"id\": \"23f75a8768250ce8\",\n        \"type\": \"ui_link\",\n        \"name\": \"Patreon\",\n        \"link\": \"https://www.patreon.com/OpenScan\",\n        \"icon\": \"fa-bookmark\",\n        \"target\": \"newtab\",\n        \"order\": 5\n    },\n    {\n        \"id\": \"b5fdd57b.15eda8\",\n        \"type\": \"ui_group\",\n        \"name\": \"Main\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": 13,\n        \"collapse\": false\n    },\n    {\n        \"id\": \"db43d646.2074c8\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"15a222ed.d70a7d\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Files&Cloud\",\n        \"icon\": \"dashboard\",\n        \"order\": 3,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"365a30d0dfa83e95\",\n        \"type\": \"ui_group\",\n        \"name\": \"settings\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": 7,\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"ac7409105cfecac6\",\n        \"type\": \"ui_group\",\n        \"name\": \"advanced\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": 7,\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"729f9ea6e3513c9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Home\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"5b3e5aca21140e9a\",\n        \"type\": \"ui_group\",\n        \"name\": \"Update\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"b3150b13e34b1fe8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"OpenScan\",\n        \"icon\": \"dashboard\",\n        \"order\": 1,\n        \"disabled\": false,\n        \"hidden\": true\n    },\n    {\n        \"id\": \"ddbd496e.93a288\",\n        \"type\": \"ui_group\",\n        \"name\": \"Manage Updates\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"3ce32450.e0cffc\",\n        \"type\": \"ui_group\",\n        \"name\": \"System & Stats\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d25e08b4.5b27e8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Update & Info\",\n        \"icon\": \"dashboard\",\n        \"order\": 4,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"4390b2ebcbbe104c\",\n        \"type\": \"ui_group\",\n        \"name\": \"General\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"8ab79a98e536e0d6\",\n        \"type\": \"ui_group\",\n        \"name\": \"Network\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"70d0be671bf03ca7\",\n        \"type\": \"ui_group\",\n        \"name\": \"Pinout\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 6,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"7a3279eea439bcdd\",\n        \"type\": \"ui_group\",\n        \"name\": \"Motor\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 5,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d324f0b852c2df0a\",\n        \"type\": \"ui_group\",\n        \"name\": \"Camera\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 4,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"12b719cba49817c9\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"457102eadc9ddb6c\",\n        \"order\": 3,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"457102eadc9ddb6c\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Settings\",\n        \"icon\": \"dashboard\",\n        \"order\": 4,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"6e339d87c7d5debe\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 1,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"33b6d7317d1524b8\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 3,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"aaf5b874c52a58aa\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 8,\n        \"width\": 7,\n        \"height\": 1\n    },\n    {\n        \"id\": \"2e08d4415665c939\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 9,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"f8d8740dcbf499fb\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 11,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"7ac0cb556740d159\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 13,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"4de2414e29020c74\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 2,\n        \"width\": 7,\n        \"height\": 1\n    },\n    {\n        \"id\": \"ac8c60543cb04139\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"ac7409105cfecac6\",\n        \"order\": 3,\n        \"width\": 7,\n        \"height\": 1\n    },\n    {\n        \"id\": \"ce21673092264c38\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3f7b77f8a1675d27\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 7,\n        \"width\": 4,\n        \"height\": 1\n    },\n    {\n        \"id\": \"0799b02d12fc3a14\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"spacer\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 25,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"bc4e2c03859196c3\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 100,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"949bafced17d66d6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"949bafced17d66d6\",\n        \"type\": \"function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"enable\",\n        \"func\": \"msg.flag = global.set('flag_pw',true)\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a1f0ed7d5a9d670e\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 110,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"544d20f02215011a\",\n                \"325314c1a24fe5b4\",\n                \"7a4a49f7dbe04e88\",\n                \"b1e2491c952f84c9\",\n                \"fac6626127bba4f5\",\n                \"bc2f0adaf72f97e9\",\n                \"ac242724fe7605a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"544d20f02215011a\",\n        \"type\": \"function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"CREATE FACTORY DEFAULT\",\n        \"func\": \"msg = { \\n'overwrite':msg.overwrite,\\n'settings':\\n    {\\n    'advanced_settings':false,\\n    'cam_awbg_blue':0,\\n    'cam_awbg_red':0,\\n    'cam_contrast':1,\\n    'cam_cropx':0,\\n    'cam_cropy':0,\\n    'cam_delay_after':0,\\n    'cam_delay_before':0,\\n    'camera':'',\\n    'cam_features' : false,\\n    'cam_focus_min': 11.5,\\n    'cam_focus_max': 11.5,\\n    'cam_gain':1,\\n    'cam_jpeg_quality':95,\\n    'cam_rotation':270,\\n    'cam_saturation':1,\\n    'cam_stacksize':1,\\n    'cam_shutter':50000,\\n    'cam_timeout':800,\\n    'cam_mask_threshold':45,\\n    'cam_mask':true,\\n    'hostname':'openscan',\\n    'model':'',\\n    'osc_credit':'',\\n    'osc_limit_filesize':'',\\n    'osc_limit_photos':'',\\n    'osc_splitsize':200000000,\\n//    'pin_extra_endstop': 19,\\n    'pin_external': 25,\\n    'pin_ringlight1': 24,\\n    'pin_ringlight2': 24,\\n    \\n    'pin_rotor_endstop': 17,\\n    'pin_rotor_dir': 23,\\n    'pin_rotor_enable': 22,\\n    'pin_rotor_step': 27,\\n    'rotor_acc': 0.5,\\n    'rotor_accramp': 500,\\n    'rotor_angle': 10,\\n    'rotor_anglemax': 75,\\n    'rotor_anglemin': -25,\\n    'rotor_anglestart': 25,\\n    'rotor_delay': 0.0002,\\n    'rotor_dir': 1,\\n    'rotor_stepsperrotation': 35200/2,\\n    'rotor_endstop_angle': 0,\\n    'rotor_endstop_enable': false,\\n\\n //   'pin_tt_endstop': 25,\\n    'pin_tt_dir': 6,\\n    'pin_tt_enable': 22,\\n    'pin_tt_step': 16,\\n    'tt_acc': 1,\\n    'tt_accramp': 200,\\n    'tt_angle': 90,\\n    'tt_delay': 0.0001,\\n    'tt_dir': 1,\\n    'tt_stepsperrotation': 1600,\\n\\n    'pin_extra_dir': 21,\\n    'pin_extra_step': 20,\\n    'pin_extra_enable': 22,\\n    'extra_acc': 1,\\n    'extra_accramp': 200,\\n    'extra_angle': 10,\\n    'extra_delay': 0.0001,\\n    'extra_dir': 1,\\n    'extra_stepsperrotation': 3200,\\n\\n    'routine_photocount':50,\\n    'routine_projectname':'default',\\n    'smb':true,\\n    'ssh':true,\\n    'status_cloud':'ready',\\n    'status_internal_cam':'--READY--',\\n    'status_uploadprogress':'',\\n    'terms':false,\\n    'token':'',\\n\\n    'uploadprogress':'',\\n    'update_type':'beta',\\n    'update_auto':true,\\n    'turntable_mode':false,\\n    'diskspace_threshold':4000,\\n    'updateable':false,\\n    'cam_focuspeak':false,\\n    'cam_histogram':false,\\n    'routine_secondpass':true,\\n    'cam_output_resolution':20000000,\\n    'cam_preview_resolution':2000000,\\n    'cam_output_downscale':false,\\n    'cam_sharparea':false,\\n    'cam_sharpness':100,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 330,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"c77552216a8bb781\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c77552216a8bb781\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"chk files\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 540,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"960912e90ba5b5bc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"960912e90ba5b5bc\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\",\n            \"1dffb799fdf10cbc\",\n            \"9fd259de91de1da1\",\n            \"fd0258418489839d\",\n            \"b4c843620c251c43\",\n            \"3876d5cbd248592b\",\n            \"a4c81754c148b86f\",\n            \"2e9b29c70969cf01\",\n            \"2477f81cddc8fa31\",\n            \"29036b35dfd672c6\",\n            \"592ec13d8f8923a9\",\n            \"cb40b9341bd22a28\",\n            \"d1efcd5fa9d25785\",\n            \"da61581182b7299e\"\n        ],\n        \"x\": 645,\n        \"y\": 60,\n        \"wires\": []\n    },\n    {\n        \"id\": \"325314c1a24fe5b4\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"create path\",\n        \"func\": \"import os\\n\\npaths = ['/home/pi/OpenScan/scans/preview/','/home/pi/OpenScan/tmp2/']\\n\\n\\nfor i in paths:\\n    if not os.path.isdir(i):\\n        os.mkdir(i)\",\n        \"outputs\": 1,\n        \"x\": 270,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"168d72a54504b327\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"5/0.1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"5\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"x\": 100,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"6c6ef2255a7d39e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6c6ef2255a7d39e5\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"repeat 5s/0.1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"61990987acd0f263\",\n            \"2415272f42ce468c\",\n            \"6bf8344af427a6ba\"\n        ],\n        \"x\": 205,\n        \"y\": 380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7a4a49f7dbe04e88\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"LED Status\",\n        \"func\": \"from OpenScan import fade_led, check_hotspot_mode, load_int\\n\\npin = load_int(\\\"pin_ringlight1\\\")\\npin2 = load_int(\\\"pin_ringlight2\\\")\\n\\nif check_hotspot_mode():\\n    msg['mode'] = True\\n    i=4\\n    j=30\\nelse:\\n    msg['mode'] = False\\n    i=2\\n    j=30\\n\\nfor x in range (i):\\n    fade_led(pin,j, 50, True)\\n    #fade_led(pin2,j, 50, True)\\n    fade_led(pin,j, 50, False)\\n    #fade_led(pin2,j, 50, False)\\n    pass\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 270,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"eb1a2387a1eeea76\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b1e2491c952f84c9\",\n        \"type\": \"function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"global\",\n        \"func\": \"global.set('light', 0)\\nglobal.set('state1', 0)\\nglobal.set('network_ssid',\\\"\\\")\\nglobal.set('network_password',\\\"\\\")\\nglobal.set('network_country',\\\"\\\")\\nglobal.set('flag_pw', true)\\nglobal.set('flag',false)\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fac6626127bba4f5\",\n        \"type\": \"function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"enable\",\n        \"func\": \"msg.enabled = true\\nmsg.payload = \\\"\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"200d4b9951b6e066\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"200d4b9951b6e066\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"enable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"c8b93b42c720b9cf\",\n            \"65518f3d4e3095e5\"\n        ],\n        \"x\": 345,\n        \"y\": 280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bc2f0adaf72f97e9\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"CAM init\",\n        \"func\": \"from OpenScan import camera\\n\\ncamera(\\\"/picam2_init\\\")\\n\\nmotor_enable_pin = 22\\n\\nimport RPi.GPIO as GPIO           # import RPi.GPIO module\\nGPIO.setmode(GPIO.BCM)            # choose BCM or BOARD\\nGPIO.setwarnings(False)\\nGPIO.setup(22, GPIO.OUT) # set a port/pin as an output\\nGPIO.output(22, 0) \\n\",\n        \"outputs\": 1,\n        \"x\": 260,\n        \"y\": 180,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"8def60b68e21e665\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"FACTORY DEFAULT\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"true\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 800,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"eb1a2387a1eeea76\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"enable LED\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"592ec13d8f8923a9\",\n            \"5baf89a2682265f7\"\n        ],\n        \"x\": 385,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0d8c6bc7887fb3c2\",\n        \"type\": \"ui_template\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"name\": \"shutdown+background\",\n        \"order\": 14,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<script id=\\\"titleScript\\\" type=\\\"text/javascript\\\">\\n    $(function() {\\n    if($('.md-toolbar-tools').length != 0){\\n        load();\\n    }else setTimeout(load, 500)\\n});\\n\\nfunction load(){\\n    $('#bmac').remove();\\n    var toolbar = $('.md-toolbar-tools');\\n    \\n    var div = $('<div/>');\\n    var p = $('<div/ id=\\\"bmac\\\">');\\n    \\n    div.append(p);\\n    div[0].style.margin = '0px 0px 0px auto';\\n    toolbar.append(div);\\n\\n    p.append(' ')\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:0 5 0 5!important;\\\" href=\\\"http://openscan:1312/shutdown\\\"><i class=\\\"fa fa-power-off\\\" ></i></a>')\\n    p.append('   ')\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:10 0 10 0!important;\\\" href=\\\"http://openscan:1312/reboot\\\"><i class=\\\"fa fa-repeat\\\" ></i></a>')\\n\\n}\\n\\n</script>\\n<style>\\n    body {\\n        background-image: url(\\\"/files/logo.jpg\\\");\\n        background-repeat: no-repeat;\\n        background-attachment: fixed;\\n        background-position: center;\\n\\n    }\\n\\n    #OpenScan_Home {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n    [node-id=\\\"a291fc98e4269c1b\\\"] {\\n        background-color: transparent !important;\\n    }\\n\\n\\n\\n    #OpenScan_Update {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n</style>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 580,\n        \"y\": 140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ac242724fe7605a6\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"rescue incomplete project\",\n        \"func\": \"#if project has not been done properly, this is a way to rescue the file\\n\\nfrom os import system\\nfrom os.path import isfile\\nfrom time import strftime\\nfrom OpenScan import load_str\\n\\nbasepath = '/home/pi/OpenScan/'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectname=load_str(\\\"routine_projectname\\\")\\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n\\nif isfile(zippath):\\n    system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\",\n        \"outputs\": 1,\n        \"x\": 310,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4468f691.103eb8\",\n        \"type\": \"ui_button\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 1,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"SCAN\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"1\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6560dd25.9e76c4\",\n        \"type\": \"ui_button\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Settings\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"3\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd5288.2805fc\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"events\": \"all\",\n        \"x\": 280,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"71e72293.91c6fc\",\n        \"type\": \"ui_button\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Files\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"2\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e7306ef2.3b4df\",\n        \"type\": \"ui_button\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Update&Info\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"4\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8955d11554f55e63\",\n        \"type\": \"ui_button\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"group\": \"5b3e5aca21140e9a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 3,\n        \"passthru\": false,\n        \"label\": \"Install Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 120,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"1e7457ea9c2c5e09\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1e7457ea9c2c5e09\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"update\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"39a502b38837273d\"\n        ],\n        \"x\": 245,\n        \"y\": 720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"245e4341d4fb611c\",\n        \"type\": \"function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"pinmap_v2\",\n        \"func\": \"msg = { \\n'overwrite':true,\\n'settings':{\\n    'pin_rotor_endstop':27,\\n    'pin_tt_endstop':5,\\n    'pin_extra_endstop':26,\\n    'pin_external':25,\\n    'pin_ringlight1':24,\\n    'pin_ringlight2':24,\\n    'pin_rotor_dir':23,\\n    'pin_rotor_enable':19,\\n    'pin_rotor_step':22,\\n    'pin_tt_dir':6,\\n    'pin_tt_enable':19,\\n    'pin_tt_step':16,\\n    'pin_extra_dir':21,\\n    'pin_extra_step':20,\\n    'pin_extra_enable':19,\\n    'extra_acc':1,\\n    'extra_accramp':200,\\n    'extra_angle':10,\\n    'extra_delay':0.0001,\\n    'extra_dir':1,\\n    'extra_stepsperrotation':3200,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 790,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"627406f3611511dc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"627406f3611511dc\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"write\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"50eeb3e362f9027f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"88b1bddde110298a\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 650,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"245e4341d4fb611c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50eeb3e362f9027f\",\n        \"type\": \"link out\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\",\n            \"b4c843620c251c43\",\n            \"3876d5cbd248592b\",\n            \"a4c81754c148b86f\",\n            \"2e9b29c70969cf01\",\n            \"2477f81cddc8fa31\",\n            \"29036b35dfd672c6\",\n            \"592ec13d8f8923a9\",\n            \"cb40b9341bd22a28\",\n            \"d1efcd5fa9d25785\",\n            \"da61581182b7299e\"\n        ],\n        \"x\": 1015,\n        \"y\": 540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4f3121f158f06a61\",\n        \"type\": \"python3-function\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"motor run\",\n        \"func\": \"from OpenScan import motorrun, load_int\\nfrom time import sleep\\n\\nmotorrun('rotor',300,True,False)\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 860,\n        \"y\": 580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4a8a04b1e5dca8fe\",\n        \"type\": \"inject\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"name\": \"run rotor till endstop\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 690,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"4f3121f158f06a61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c8167775e3401fad\",\n        \"type\": \"ui_template\",\n        \"z\": \"e6f4d02efb300ea9\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"name\": \"infotext\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"format\": \"<h2>What's new?</h2>\\n<ul>\\n    <li>speed improvement 2-3x</li>\\n    <li>currently tested on OpenScan Mini + IMX519 with RPi 4</li>\\n    <li>optimized toolpath</li>\\n    <li>more responsive user interface</li>\\n    <li>hotspot mode (when no wireless network available ssid: <em>openscan</em> pw: <em>opensource</em></li>\\n    <li>preview features and sharpness</li>\\n    <li>partial background masking</li>\\n    <li>no more autofocus --&gt; instead you can set a min and max focus distance</li>\\n</ul>\\nnote, that this is still an early beta and there might be some unintended bugs. please reach out to info@openscan.eu if you run into any issues.\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 580,\n        \"y\": 260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6a3d9acbe097a3d2\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"cb6ebdabaaf7d0da\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7ef6f1b5c67201fe\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\n\\nvar file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"86f7d1b2d763f6e2\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"c8a3fde5206ce1ae\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fd799c931139764d\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 240,\n        \"wires\": [\n            [\n                \"87be854db758a9a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d5140d455122c49a\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"9daea4bd57f7a00e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"194f3590dd4f6e3d\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\nvar file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 240,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2de69452e829d780\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\nvar file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"58e565fea35cb667\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 3,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 320,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"734ac3bff2df6837\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"97170908e1f4ac55\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload=\\\"default\\\"\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"58e565fea35cb667\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"734ac3bff2df6837\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\n\\nvar file = 'routine_projectname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload).replace(/ /g, '_')\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 80,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1dffb799fdf10cbc\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 55,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"97170908e1f4ac55\",\n                \"6a3d9acbe097a3d2\",\n                \"86f7d1b2d763f6e2\",\n                \"fd799c931139764d\",\n                \"d5140d455122c49a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a0156eaac7dd35e5\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"shutter\",\n        \"func\": \"from time import time, sleep\\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\\nimport math\\n\\n\\ncamera('/picam2_exposure?exposure=' + str(int(msg['payload']*1000)))\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c7f5808d753480d4\",\n        \"type\": \"inject\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"6\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 170,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"11f41a6030578ef4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"11f41a6030578ef4\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 310,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"a0156eaac7dd35e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"855cbcadef1163c5\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"enable\",\n        \"func\": \"msg.light = global.get('light')\\nmsg.state1 = global.get('state1')\\nmsg.flag = global.get('flag')\\n\\n\\nvar min = 1;\\nvar max = 100000;\\nvar random = Math.floor(Math.random() * (max - min + 1)) + min;\\n\\nvar formatted = random.toString().padStart(3, '0');\\nmsg.payload=\\\"/tmp2/preview.jpg?ts=\\\" + Date.now().toString();\\n\\nif (global.get('flag_pw') == false){\\n    if (msg.flag == true){\\n        return msg\\n    }\\n    return \\n}\\nelse{\\n    return msg\\n}\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"d1b87196ae5373ed\",\n                \"41e6a4649b6afbfb\",\n                \"2fd24f8e8e9c08b7\",\n                \"85a268108250ba88\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1a443e20a973d2f1\",\n        \"type\": \"change\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"flag_pw true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 630,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d1b87196ae5373ed\",\n        \"type\": \"change\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"flag_pw false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 430,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"03d92601c62b79d4\",\n        \"type\": \"inject\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"4s/0.5\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"4\",\n        \"topic\": \"Repeat\",\n        \"payload\": \"0.1\",\n        \"payloadType\": \"str\",\n        \"x\": 100,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"855cbcadef1163c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"41e6a4649b6afbfb\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"Take Preview Shot\",\n        \"func\": \"from time import time, sleep\\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\\n\\nstatus = load_str('status_internal_cam')\\n\\n#return msg\\n\\nmsg['payload']=\\\"/tmp2/preview.jpg?ts=\\\"+str(int(time()))\\n\\nif msg['flag'] == True:\\n    return msg\\n\\n\\n#if status!=\\\"--READY--\\\":\\n#    return msg\\n\\n#msg['preview'] = True\\n\\ncamera('/picam2_take_photo')\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 450,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"1a443e20a973d2f1\",\n                \"296636b7467fc745\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"85a268108250ba88\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"preview_arducam\",\n        \"order\": 1,\n        \"width\": 7,\n        \"height\": 9,\n        \"format\": \"<style>\\n  /* Define the color classes */\\n  .btn-0 {\\n    opacity: 0.4;\\n  }\\n\\n  .btn-1 {\\n    opacity: 1;\\n\\n  }\\n\\n  .btn-2 {\\n    opacity: 1;\\n  }\\n</style>\\n\\n<div style=\\\"position: absolute; top: 0; left: 0; width: 100%; height: 100%;\\\">\\n  <img ng-src=\\\"{{msg.payload}}\\\" style=\\\"width: 100%; height: 100%; object-position:top; object-fit: contain; vertical-align: top;\\\"/>\\n  <div style=\\\"position: absolute; top: 3%; right: 3% ; height: 30% \\\">\\n    <md-button class=\\\"button save\\\"\\n      style=\\\"color: default; padding: 10px; margin: 0px 0px 0px 0px; width:100%; height: 27%;\\\"\\n      ng-class=\\\"{'btn-0': msg.light == 0, 'btn-1': msg.light == 1, 'btn-2': msg.light == 2}\\\"\\n      ng-click=\\\"send({light:toggleState()})\\\"><i class=\\\"fa fa-lightbulb-o fa-2x\\\"></i></md-button>\\n  </div>\\n  <div style=\\\"position: absolute; top: 13%; right: 3% ; width:10%; height: 30% \\\">\\n    <md-button class=\\\"button save\\\"\\n      style=\\\"color: default; padding: 10px; margin: 0px 0px 0px 0px; width:100%; height: 27%;\\\"\\n      ng-class=\\\"{'btn-0': msg.state1 == 0, 'btn-1': msg.state1 == 2, }\\\" ng-click=\\\"send({state1:toggleState1()})\\\">\\n      <i class=\\\"fa fa-cog\\\"></i>\\n    </md-button>\\n  </div>\\n\\n  <div\\n    style=\\\"position: absolute; top: 20px; left: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 130px; height: 130px;\\\">\\n\\n\\n\\n    <div\\n      style=\\\"display: flex; justify-content: center; align-items: center; width: 100%; height: 33.33%; margin-bottom: 10px;\\\">\\n      <md-button class=\\\"button save\\\"\\n        style=\\\"color: default; padding: 10px; margin: 0px 0px 0px 0px; width: 33.33%; height: 100%;\\\"\\n        ng-click=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-arrow-up\\\"></i></md-button>\\n    </div>\\n    <div\\n      style=\\\"display: flex; justify-content: space-between; align-items: center; width: 100%; height: 33.33%; margin-bottom: 10px;\\\">\\n      <md-button class=\\\"button save\\\" style=\\\"color: default; padding: 10px; margin: 0; width: 33.33%; height: 100%;\\\"\\n        ng-click=\\\"send({payload: 'left'})\\\"><i class=\\\"fa fa-arrow-left\\\"></i></md-button>\\n      <md-button class=\\\"button save\\\"\\n        style=\\\"opacity: 0; color: default; padding: 10px; margin: 0; width: 33.33%; height: 100%; pointer-events: none;\\\">\\n      </md-button>\\n      <md-button class=\\\"button save\\\" style=\\\"color: default; padding: 10px; margin: 0; width: 33.33%; height: 100%;\\\"\\n        ng-click=\\\"send({payload: 'right'})\\\"><i class=\\\"fa fa-arrow-right\\\"></i></md-button>\\n    </div>\\n    <div style=\\\"display: flex; justify-content: center; align-items: center; width: 100%; height: 33.33%;\\\">\\n      <md-button class=\\\"button save\\\"\\n        style=\\\"color: default; padding: 10px; margin: 0px 0px 0px 0px; width: 33.33%; height: 100%;\\\"\\n        ng-click=\\\"send({payload: 'down'})\\\"><i class=\\\"fa fa-arrow-down\\\"></i></md-button>\\n    </div>\\n  </div>\\n</div>\\n\\n<script>\\n  // Define the button state and label\\nvar state = 1;\\nvar state1 = 0;\\n\\n// Initialize the button state and label\\nfunction init() {\\n    // Get the current state from the incoming message, if any\\n    var msg = scope.$parent.msg;\\n    if (msg) {\\n        state = msg.light;\\n        state1 = msg.state1\\n    }\\n}\\n\\n// Toggle the button state and label\\nfunction toggleState() {\\n    state = (state + 1) % 2\\n    return state\\n}\\nfunction toggleState1() {\\n    state1 = (state1 + 1) % 2\\n    return state1\\n}\\nscope.toggleState1 = toggleState1;\\nscope.state1 = state1;\\nscope.toggleState = toggleState;\\nscope.state = state;\\n</script>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"417f653ca0dfdcfc\",\n                \"180476141c2a44ad\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"296636b7467fc745\",\n        \"type\": \"link out\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"link out 1\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2c58a1a66c4a8c11\"\n        ],\n        \"x\": 575,\n        \"y\": 800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"417f653ca0dfdcfc\",\n        \"type\": \"delay\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 640,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"e864254b18c23dd1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e864254b18c23dd1\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"motorrun\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nif 'payload' not in msg:\\n    return\\n\\nif msg['payload'] == \\\"up\\\":\\n    motorrun('rotor',load_int('rotor_angle'))\\nif msg['payload'] == \\\"down\\\":\\n    motorrun('rotor',-load_int('rotor_angle'))\\nif msg['payload'] == \\\"left\\\":\\n    motorrun('tt',load_int('tt_angle'))\\nif msg['payload'] == \\\"right\\\":\\n    motorrun('tt',-load_int('tt_angle'))\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 780,\n        \"y\": 840,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"180476141c2a44ad\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"global\",\n        \"func\": \"if (typeof msg.light !== \\\"undefined\\\"){\\n    global.set('light',msg.light)\\n}\\nif (typeof msg.state1 !== \\\"undefined\\\"){\\n    global.set('state1',msg.state1)\\n}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"8cbdbfecbd12ef83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1fe18f3b0b52aabd\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"LED\",\n        \"func\": \"from OpenScan import ringlight\\nfrom time import time\\n\\nstarttime = time()\\n\\nif 'light' in msg:\\n    val = msg['light']\\n    while time()-starttime<0.02:\\n        if val == 0:\\n            ringlight(1,False)\\n            ringlight(2,False)\\n\\n        elif val == 1:\\n            ringlight(1,True)\\n            ringlight(2,True)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 870,\n        \"y\": 880,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2fd24f8e8e9c08b7\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"load advanced\",\n        \"func\": \"from OpenScan import load_bool\\n\\nif 'state1' in msg:\\n    if msg['state1'] == 0:\\n        msg['payload']={\\\"group\\\":{\\\"hide\\\":[\\\"Scan_advanced\\\"],\\\"show\\\":[]}}\\n    else:\\n        msg['payload']={\\\"group\\\":{\\\"hide\\\":[],\\\"show\\\":[\\\"Scan_advanced\\\"]}}\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 440,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"923be3b2b25224b4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"923be3b2b25224b4\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"change visibility\",\n        \"events\": \"all\",\n        \"x\": 640,\n        \"y\": 720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8a3fde5206ce1ae\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"name\": \"shutter\",\n        \"order\": 4,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0.1;\\n        scope.maxValue = 300;\\n        scope.stepValue = 0.1;\\n        scope.sliderName =\\\"Shutterspeed (ms)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"034ec9f59e50a361\",\n                \"a0156eaac7dd35e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"034ec9f59e50a361\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"rate\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: msg.payload });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n} else {\\n    context.set(\\\"lastMessage\\\", msg.payload);\\n    clearTimeout(context.timeout);\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: context.get(\\\"lastMessage\\\") });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n}\\nvar file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n    if (err) {\\n        return\\n    }\\n});\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 160,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"87be854db758a9a6\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"name\": \"Cropy\",\n        \"order\": 7,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"\\n          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0;\\n        scope.maxValue = 100;\\n        scope.stepValue = 1;\\n        scope.sliderName = \\\"Crop Y (%)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 240,\n        \"wires\": [\n            [\n                \"194f3590dd4f6e3d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9daea4bd57f7a00e\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"name\": \"Cropx\",\n        \"order\": 6,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0;\\n        scope.maxValue = 100;\\n        scope.stepValue = 1;\\n        scope.sliderName = \\\"Crop x (%)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"2de69452e829d780\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb6ebdabaaf7d0da\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"name\": \"Photos\",\n        \"order\": 5,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 10;\\n        scope.maxValue = 400;\\n        scope.stepValue = 10;\\n        scope.sliderName =\\\"Photos\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"7ef6f1b5c67201fe\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"82ecd3cd971cb7ea\",\n        \"type\": \"ui_text\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"projectname\",\n        \"label\": \"Projectname\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 530,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ed2974731fb8a84e\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"ac7409105cfecac6\",\n        \"name\": \"threshold\",\n        \"order\": 5,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 1;\\n        scope.minValue = 0;\\n        scope.maxValue = 255;\\n        scope.stepValue = 1;\\n        scope.sliderName =\\\"Mask Threshold (1-255)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"06e1e19835a9816e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8cbdbfecbd12ef83\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"led\",\n        \"func\": \"from OpenScan import fade_led, ringlight, load_int\\n\\npin = load_int('pin_ringlight1')\\n\\n\\nif 'light' in msg:\\n    val = msg['light']\\n\\n    if val ==1:\\n        fade_led(pin,50, 100, True)\\n\\n    else:\\n        fade_led(pin,50, 100, False)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 750,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"1fe18f3b0b52aabd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"06e1e19835a9816e\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\nvar file = 'cam_mask_threshold'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 470,\n        \"y\": 520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2d5b1eb4380ae5a8\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_mask_threshold'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"ed2974731fb8a84e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dd287f40385922f\",\n        \"type\": \"ui_button\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start \",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-play\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"enabled\",\n        \"topicType\": \"str\",\n        \"x\": 130,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"33d94a04b96a2de0\",\n                \"6d15f717d5a11002\",\n                \"9a6b30a0175a8ecd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"579f2211199fd6ab\",\n        \"type\": \"ui_button\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"stop\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 12,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-stop\",\n        \"payload\": \"numberofphotos\",\n        \"payloadType\": \"global\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 490,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"1787f08ed7070ddd\",\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1787f08ed7070ddd\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"stop\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nsave('status_internal_cam', 'Routine-stopping')\",\n        \"outputs\": 1,\n        \"x\": 630,\n        \"y\": 1100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b13dfd9f8d3711\",\n        \"type\": \"link out\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"c8b93b42c720b9cf\"\n        ],\n        \"x\": 395,\n        \"y\": 1000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9654deebb668e012\",\n        \"type\": \"inject\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 290,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8367cfa0bf5bc5df\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start routine\",\n        \"links\": [\n            \"200d4b9951b6e066\",\n            \"8689e938.dd9e38\",\n            \"e9b13dfd9f8d3711\",\n            \"f20f2dbc.0f123\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 45,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"7dd287f40385922f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fb13752beddee9f2\",\n        \"type\": \"link out\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"c8b93b42c720b9cf\"\n        ],\n        \"x\": 525,\n        \"y\": 1040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"33d94a04b96a2de0\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"enable\",\n        \"func\": \"global.set('flag', false)\\n\\nvar file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nvar data = fs.readFileSync(filepath+file, 'utf8');\\n\\n\\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\\n    return\\n}\\n\\nmsg.enabled = true\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c1c044f3c2139f68\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 490,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1daf9e3a5bd5ab48\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"msg\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', false)\\nmsg.enabled = true\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 430,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"fb13752beddee9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d15f717d5a11002\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nmsg.payload = false\\nglobal.set(\\\"flag\\\",true)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"e9b13dfd9f8d3711\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a6b30a0175a8ecd\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"Routine\",\n        \"func\": \"from OpenScan import load_bool, load_str, load_int, load_float, motorrun, sort_spherical_coordinates_deg, create_coordinates, take_photo, save, \\\\\\n    load_bool, camera\\nfrom time import sleep, strftime, time\\nfrom subprocess import getoutput\\n\\nfrom zipfile import ZipFile, ZIP_DEFLATED\\nfrom os import system\\nfrom os.path import isfile, getsize\\nimport math\\nimport threading\\nimport numpy as np\\n\\nif load_str(\\\"status_internal_cam\\\") == \\\"no camera found\\\" or load_str(\\\"status_internal_cam\\\")[:5] == \\\"Featu\\\":\\n    return\\n\\n#motorrun('rotor', 140, ES_enable=True, ES_start_state=True)\\n#motorrun('rotor', 10)\\n\\n\\n\\nsave('status_internal_cam', 'Routine-preparing')\\ncamera('/picam2_switch_mode?mode=1')\\n\\nsave('cam_sharparea', False)\\nsave('cam_features', False)\\n\\n\\nprojectname = load_str(\\\"routine_projectname\\\")\\nangle_max = load_int('rotor_anglemax')\\nangle_min = load_int('rotor_anglemin')\\nif load_bool('rotor_enable_endstop'):\\n    angle_start = load_int('rotor_endstop_angle')\\n    motorrun('rotor',angle_start/abs(angle_start) * 130, True, False)\\n\\nelse:\\n    angle_start = load_int('rotor_anglestart')\\n\\n\\nphotocount = load_int('routine_photocount')\\n\\nfocus_min = load_float('cam_focus_min')\\nfocus_max = load_float('cam_focus_max')\\nstacksize = load_int('cam_stacksize')\\n\\nif focus_min == focus_max:\\n    stacksize = 1\\n\\nfocuslist = []\\nif stacksize == 1:\\n    steps = 3 + int(abs(focus_max-focus_min)*0.8)\\nelse:\\n    steps = stacksize\\n\\nfor i in range (steps):\\n    focuslist.append(min(focus_min,focus_max) + i * abs(focus_max-focus_min)/(steps-1))\\n\\nmsg['focuslist'] = focuslist\\nmsg['payload2'] = []\\ncounter = 0\\n\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp2/preview.jpg'\\nzippath = basepath + 'tmp.zip'\\n\\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n\\nif isfile(zippath):\\n    system('rm ' + zippath)\\nsleep(1)\\n\\ncoordinates = create_coordinates(angle_min, angle_max, photocount)\\ncoordinates = sort_spherical_coordinates_deg(coordinates)\\n\\nmsg['payload'] = coordinates\\n\\nposition_last = (angle_start, 0)\\n\\nzip = ZipFile(zippath, \\\"a\\\", ZIP_DEFLATED, allowZip64=True)\\n\\nstarttime = time()\\n\\ndef photo(counter2):\\n    camera('/picam2_take_photo')\\n    returning[0] = focus(returning[0])\\n    zip.write(temppath, projectname + '_' + str(counter) + \\\".jpg\\\")\\n\\ndef stack_photo(i):\\n    \\n    camera('/picam2_take_photo')\\n    name = projectname + '_' + str(counter) + \\\"-\\\" + str(i) + \\\".jpg\\\"\\n\\n    zip.write(temppath, name)\\n    \\ndef stack_focus(i):\\n    sleep(load_float('cam_shutter')/1000000*2)\\n    if i < len(focuslist)-1:\\n        camera('/picam2_focus?focus=' + str(focuslist[i+1]))\\n    else:\\n        camera('/picam2_focus?focus=' + str(focuslist[0]))\\n    sleep(1.7)\\n\\ndef photo_stack():\\n    camera('/picam2_focus?focus=' + str(focuslist[0]))\\n    for i in range(len(focuslist)):\\n        if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n            break\\n        save('status_internal_cam', 'Routine-Photo ' + str(counter) + '/' + str(photocount) + \\\"-F\\\"+ str(i+1))\\n        \\n        focus_thread = threading.Thread(target=stack_focus, args=(i,))\\n        photo_thread = threading.Thread(target=stack_photo, args=(i,))\\n        \\n        focus_thread.start()\\n        photo_thread.start()\\n        \\n        focus_thread.join()\\n        photo_thread.join()\\n\\n\\n\\ndef move_motor():\\n    rotor_angle = position[0] - position_last[0]\\n    msg['payload2'].append(rotor_angle)\\n    #if abs(rotor_angle) > 180:\\n    #    rotor_angle = -360 * rotor_angle / abs(rotor_angle) + rotor_angle\\n    tt_angle = position_last[1] - position[1]\\n    if tt_angle > 180:\\n        tt_angle -= 360\\n    elif tt_angle < -180:\\n        tt_angle += 360\\n\\n    motorrun('tt',tt_angle)\\n    motorrun('rotor',rotor_angle)\\n    return\\n\\n    # THE FOLLOWING DOES NOT WORK PROPERLY WITH THREADING ?!\\n\\n    #tt_thread = threading.Thread(target=motorrun, args=('tt', tt_angle))\\n    #rotor_thread = threading.Thread(target=motorrun, args=('rotor', rotor_angle))\\n    #tt_thread.start()\\n    #rotor_thread.start()\\n    #tt_thread.join()\\n    #rotor_thread.join()\\n\\n\\ncounter2 = 0\\n\\ndef check_diskspace():\\n    diskspace_threshold = load_int('diskspace_threshold')\\n    diskspace = getoutput('df -h / | awk \\\"{print $5}\\\"').split('\\\\n')[1]\\n    available = int(float(diskspace.replace(' ','').split('G')[2])*1000)\\n    if available < diskspace_threshold:\\n        save('status_internal_cam', 'Routine-stopping')\\n    return\\n\\ndef focus(i):\\n    f = focuslist[i]\\n    camera('/picam2_focus?focus=' + str(f))\\n    if i < len(focuslist) - 1:\\n        i += 1\\n    else:\\n        i = 0\\n    return i\\n\\n\\nfor position in coordinates:\\n    counter += 1\\n    filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \\\".jpg\\\"\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n    if counter < 6:\\n        ETA = ''\\n\\n    \\n    save('status_internal_cam', 'Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\\n    if counter > 6:\\n        check_diskspace()\\n\\n    move_motor()\\n    sleep(load_float(\\\"cam_delay_before\\\"))\\n    \\n    if stacksize ==1:\\n        returning = [counter2]\\n        photo(returning)\\n        counter2 = returning[0]\\n\\n    else:\\n        photo_stack()\\n\\n    sleep(load_float(\\\"cam_delay_after\\\"))\\n    ETA = '-ETA:' + str(int((photocount / counter - 1) * (time() - starttime))) + '/' + str(\\n        int(photocount / counter * (time() - starttime))) + 's'\\n    position_last = position\\n\\nzip.close()\\ncamera('/picam2_switch_mode?mode=0')\\n\\nsave('status_internal_cam', 'Routine-done')\\n\\nmotorrun('rotor', -position_last[0] )\\nmotorrun('tt', position_last[1])\\n\\nsave('status_internal_cam', '--READY--')\\n\\nsystem('mv ' + zippath + \\\" \\\" + basepath + \\\"scans/\\\" + projectcode + \\\".zip\\\")\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 300,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"1daf9e3a5bd5ab48\",\n                \"795c85ad4f109567\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"afe47a9eaae6f67f\",\n        \"type\": \"ui_text\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"365a30d0dfa83e95\",\n        \"order\": 1,\n        \"width\": 7,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Current Status:\",\n        \"format\": \"<font color= {{msg.color}} > {{msg.payload}} </font>\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 340,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8608517d0567d63f\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\n\\nif (data === 'no camera found'){\\n    msg.color = 'red'\\n}\\n\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"afe47a9eaae6f67f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6bf8344af427a6ba\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start status\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 55,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"8608517d0567d63f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"78cfe60013a1bea4\",\n        \"type\": \"ui_switch\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"label\": \"Show Sharpness\",\n        \"tooltip\": \"\",\n        \"group\": \"ac7409105cfecac6\",\n        \"order\": 2,\n        \"width\": 7,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 350,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"9774e7ad3b506354\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9774e7ad3b506354\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import save\\nsave('cam_sharparea',msg['payload'])\\n\\nif not 'flag' in msg:\\n    msg['flag'] = True\\n    msg['payload'] = False\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"f0af909f3e739b22\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"39c744466a21735e\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"focus_min\",\n        \"order\": 3,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 11.5;\\n        scope.minValue = 1;\\n        scope.maxValue = 15;\\n        scope.stepValue = 0.1;\\n        scope.sliderName =\\\"Focus_min\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 990,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"fa181d22775c2ce6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"61aab497fa50898e\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"focus_max\",\n        \"order\": 4,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 11.5;\\n        scope.minValue = 1;\\n        scope.maxValue = 15;\\n        scope.stepValue = 0.1;\\n        scope.sliderName =\\\"Focus_max\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 990,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"c615034ea6b26174\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5e83b653850fa16e\",\n        \"type\": \"ui_template\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"group\": \"ac7409105cfecac6\",\n        \"name\": \"stacksize\",\n        \"order\": 4,\n        \"width\": 7,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 1;\\n        scope.minValue = 1;\\n        scope.maxValue = 20;\\n        scope.stepValue = 1;\\n        scope.sliderName =\\\"Stacking\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"237c2135cdad86ea\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dd7fb8791d34c751\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"enable\",\n        \"func\": \"global.set('light', 1)\\nmsg.light = 1\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"180476141c2a44ad\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5baf89a2682265f7\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"enable led\",\n        \"links\": [\n            \"eb1a2387a1eeea76\"\n        ],\n        \"x\": 145,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"dd7fb8791d34c751\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6a26e8a7253d708c\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_focus_min'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 830,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"39c744466a21735e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"35ad7e55833836c1\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_focus_max'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 830,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"61aab497fa50898e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9fd259de91de1da1\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 735,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"6a26e8a7253d708c\",\n                \"35ad7e55833836c1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fa181d22775c2ce6\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"rate\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: msg.payload });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n} else {\\n    context.set(\\\"lastMessage\\\", msg.payload);\\n    clearTimeout(context.timeout);\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: context.get(\\\"lastMessage\\\") });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n}\\nvar file = 'cam_focus_min'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n    if (err) {\\n        return\\n    }\\n    return msg\\n});\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1150,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"ae5ee8787145906d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c615034ea6b26174\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"rate\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: msg.payload });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n} else {\\n    context.set(\\\"lastMessage\\\", msg.payload);\\n    clearTimeout(context.timeout);\\n    context.timeout = setTimeout(() => {\\n        node.send({ payload: context.get(\\\"lastMessage\\\") });\\n        clearTimeout(context.timeout);\\n        delete context.timeout;\\n    }, delay);\\n}\\nvar file = 'cam_focus_max'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n    if (err) {\\n        return\\n    }\\n    return msg\\n});\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1150,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"ae5ee8787145906d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ae5ee8787145906d\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import camera\\ncamera('/picam2_focus?focus=' + str(msg['payload']))\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 1290,\n        \"y\": 60,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f0af909f3e739b22\",\n        \"type\": \"ui_switch\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"label\": \"Show Features\",\n        \"tooltip\": \"\",\n        \"group\": \"ac7409105cfecac6\",\n        \"order\": 1,\n        \"width\": 7,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 340,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"710fc2dbb5ef0167\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"710fc2dbb5ef0167\",\n        \"type\": \"python3-function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import save\\nsave('cam_features',msg['payload'])\\n\\n\\nif not 'flag' in msg:\\n    msg['flag'] = True\\n    msg['payload'] = False\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"78cfe60013a1bea4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d93c2b67bcf23b9a\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"5e83b653850fa16e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"237c2135cdad86ea\",\n        \"type\": \"function\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"write\",\n        \"func\": \"const delay = 100; // 100 ms delay\\n\\nif (!context.timeout) {\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: msg.payload });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n} else {\\n  context.set(\\\"lastMessage\\\", msg.payload);\\n  clearTimeout(context.timeout);\\n  context.timeout = setTimeout(() => {\\n    node.send({ payload: context.get(\\\"lastMessage\\\") });\\n    clearTimeout(context.timeout);\\n    delete context.timeout;\\n  }, delay);\\n}\\nvar file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 470,\n        \"y\": 480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fd0258418489839d\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 95,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"2d5b1eb4380ae5a8\",\n                \"d93c2b67bcf23b9a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6f281351e11b58a\",\n        \"type\": \"inject\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"enabled\",\n                \"v\": \"true\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"x\": 190,\n        \"y\": 600,\n        \"wires\": [\n            [\n                \"ed2974731fb8a84e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ca4ca7fae36d312d\",\n        \"type\": \"inject\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"enabled\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"x\": 190,\n        \"y\": 640,\n        \"wires\": [\n            [\n                \"ed2974731fb8a84e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c8b93b42c720b9cf\",\n        \"type\": \"link in\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"sharpness/features\",\n        \"links\": [\n            \"200d4b9951b6e066\",\n            \"e9b13dfd9f8d3711\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 85,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"78cfe60013a1bea4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"795c85ad4f109567\",\n        \"type\": \"debug\",\n        \"z\": \"481edaf6db5a7a54\",\n        \"name\": \"debug 5\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"false\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 620,\n        \"y\": 1000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ea54fcc2.cfcc2\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"get dirs\",\n        \"func\": \"from glob import glob\\nimport os\\nfrom zipfile import ZipFile\\nfrom datetime import datetime\\nfrom PIL import Image\\n\\ndef set_stats(stat):\\n    try:\\n        with open(directory+set[:-4]+\\\"/\\\"+stat,\\\"r\\\") as file:\\n            stat=file.read()\\n    except:\\n        stat=\\\"\\\"\\n    return stat\\n\\ntable=[]\\ndirectory=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nfor d in glob(directory+\\\"*.zip\\\"):\\n    set=os.path.basename(d)\\n\\n    try:\\n        with ZipFile(d, 'r') as f:\\n            photos = len(f.namelist())\\n        \\n            if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\\n                image = f.open(f.namelist()[int(photos/2)])\\n                img = Image.open(image)\\n                width, height = img.size\\n                width_factor = width/300\\n                height_factor = height/295\\n                if height_factor>=width_factor and height_factor > 1:\\n                    new_size=(int(width/height_factor), int(height/height_factor))\\n                    img = img.resize(new_size)\\n                elif height_factor<width_factor and width_factor > 1:\\n                    new_size=(int(width/width_factor),int(height/width_factor))\\n                    img = img.resize(new_size)\\n                img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\\n        list=[]\\n        for fi in f.filelist:\\n            list.append(f.getinfo(fi.filename).date_time)\\n        \\n        duration = str(datetime(*max(list)) - datetime(*min(list)))\\n        \\n        size = float(int(float(os.path.getsize(d))/100000))/10\\n        size_full= os.path.getsize(d)\\n        status=set_stats(\\\"status\\\")\\n        expiration=set_stats(\\\"expiration\\\")\\n        download=set_stats(\\\"download\\\")\\n    \\n        if len(download)!=0:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Download\\\":\\\"<a href=\\\"+download+\\\">RESULT</a>\\\",\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n                })\\n        else:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n\\n                })\\n    except:\\n        pass\\n\\nmsg['payload']=table\\nmsg['topic']=\\\"\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 480,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"f3662f8c7d3d7a2d\",\n                \"01e4783e148c6698\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2f4c0f98.dee2\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"filelist\",\n        \"links\": [\n            \"50eeb3e362f9027f\",\n            \"960912e90ba5b5bc\",\n            \"a4f09e25.02569\",\n            \"ed35109311335099\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 355,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"952ce286.4ffd4\",\n        \"type\": \"ui_text\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Status\",\n        \"label\": \"Status\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 250,\n        \"y\": 60,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d4383424.7807c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"upload\",\n        \"func\": \"import os\\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\\nfrom subprocess import getoutput\\n\\nbasedir = '/home/pi/OpenScan/'\\n\\nif load_str(\\\"feedback_terms\\\")==\\\"False\\\":\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic'] = 'OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nmsg = msg['payload']\\n\\ndef upload(filelist, ulinks):\\n    pid = getoutput('pidof curl')\\n    if pid != \\\"\\\":\\n        os.system('kill ' + pid)\\n\\n    i = 0\\n    for file in filelist:\\n        link = ulinks[i]\\n        save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\\n        cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\\\"' + file + '\\\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\\n        i = i+1\\n        os.system(cmd)\\n\\n########\\nif not os.path.isfile(basedir + 'settings/token'):\\n    msg['flag'] = True\\n    save('status_cloud', 'please enter token first')\\n    return msg\\nwith open(basedir + 'settings/token', 'r') as file:\\n    token = file.read().strip('\\\\n')\\n\\n########\\nr = OpenScanCloud('getTokenInfo', {'token':token})\\n\\nif r.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    save('status_cloud', 'invalid/missing token')\\n    return None,msg\\nelif r.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\n########\\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\\n    msg['flag'] = True\\n    save('status_cloud', 'limit(s) exceeded')\\n    return msg\\n\\n########\\ntemp =  OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\\nif temp.status_code not in (200,401):\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nif temp.status_code != 401:\\n    temp = temp.json()\\n    if 'status' in temp:\\n        if temp['status'] != 'created':\\n            save('status_cloud','already exists')\\n            with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\\n                file.write(temp['status'])\\n            return msg\\n#####\\n\\nmsg2={}\\nmsg2['token'] = token\\nmsg2['parts'] = 1\\nmsg['partslist']=[]\\n\\n#######\\nsize_to_split = load_int('osc_splitsize')\\n\\nif msg['Size_full'] > size_to_split:\\n    tempdir = basedir + 'tmp/split/'\\n    if os.path.isdir(tempdir):\\n        os.system('rm -r ' + tempdir)\\n    os.mkdir(tempdir)\\n    save('status_cloud', 'zipping files, please wait ...')\\n    cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\\n    os.system(cmd)\\n    save('status_cloud', 'zip done')\\n    list = os.listdir(tempdir)\\n    for l in list:\\n        msg['partslist'].append(tempdir + l)\\n        msg['partslist'].sort()\\n    msg2['parts']=len(msg['partslist'])\\nelse:\\n    msg['partslist'] = [basedir + 'scans/' +msg['Set']]\\n\\n#######\\nmsg2['photos'] = msg['Photos']\\nmsg2['filesize'] = msg['Size_full']\\nmsg2['project'] = msg['Set']\\n\\nr = OpenScanCloud('createProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\\n    os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\\n    file.write('prepared')\\n\\nsave('status_cloud', 'uploading')\\nupload(msg['partslist'], msg1['ulink'])\\n\\nr = OpenScanCloud('startProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Upload failed'\\n    msg['payload'] = 'please try again'\\n    save('status_cloud', 'upload failed')\\n    return None,msg\\n\\nsave('status_cloud', 'uploaded')\\n\\nsave('status_cloud', 'project started')\\n\\ntry:\\n    os.system('rm -r ' + tempdir)\\nexcept:\\n    pass\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 530,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"9a132ab1.b21658\"\n            ],\n            [\n                \"3d16b3789632784d\",\n                \"9a132ab1.b21658\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50710948.71c308\",\n        \"type\": \"change\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"set\",\n                \"pt\": \"global\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 750,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ada1b6f7cccc9344\",\n                \"85839a17fb7b58b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"834046a4.647938\",\n        \"type\": \"ui_text\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Set\",\n        \"label\": \"Set:\",\n        \"format\": \"{{msg.payload.Name}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9a132ab1.b21658\",\n        \"type\": \"change\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"flag.true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 780,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"8689e938.dd9e38\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3c67e97b.9d19a6\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"enable\",\n        \"func\": \"//if (global.get('flag') === false){\\n//    msg.enabled = false\\n//    msg.color=\\\"white\\\"\\n//}\\n//else{\\n\\n    msg.enabled = true\\n    msg.color=\\\"red\\\"\\n    \\n//}\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 130,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"7a93d1e18254685c\",\n                \"e434ef42bd6b92e8\",\n                \"d5d840183025d91b\",\n                \"ab9e90ab5a53a0dd\",\n                \"478994f671a3907d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bfc01f26.c32cf\",\n        \"type\": \"change\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"flag.false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 420,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"f20f2dbc.0f123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b33d604c.5f1a6\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"enable cloud\",\n        \"links\": [\n            \"4082b136.dae18\",\n            \"8689e938.dd9e38\",\n            \"bd75f33b8a57c522\",\n            \"e9b13dfd9f8d3711\",\n            \"f20f2dbc.0f123\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 35,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"3c67e97b.9d19a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f6bd1a04.470838\",\n        \"type\": \"change\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"payload\",\n                \"pt\": \"msg\",\n                \"to\": \"set\",\n                \"tot\": \"global\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 410,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"d4383424.7807c8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4082b136.dae18\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"links\": [\n            \"b33d604c.5f1a6\",\n            \"87574a42938afec4\"\n        ],\n        \"x\": 715,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f20f2dbc.0f123\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 515,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8689e938.dd9e38\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 875,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15de0ebb.616d61\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a7d89487.ee8858\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a7d89487.ee8858\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\ntry:\\n    os.remove(dir+msg['Set'])\\n    shutil.rmtree(dir+msg['Set'][:-4])\\nexcept:\\n    pass\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4f09e25.02569\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\"\n        ],\n        \"x\": 775,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7a93d1e18254685c\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"92c98e6ce7cd25f9\"\n        ],\n        \"x\": 235,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d99c601c9881680\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"refresh\",\n        \"func\": \"from time import sleep\\nimport os\\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\n\\nif load_bool(\\\"terms\\\")==False:\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic']='OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nsave('status_cloud','refreshing')\\ntoken = load_str('token')\\n\\ntest = OpenScanCloud('getTokenInfo',{'token':token})\\nif test.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    return None,msg\\nelif test.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nstats = test.json()\\nfor i in stats:\\n    save('osc_'+i, stats[i])\\n    pass\\n\\nmsg={}\\nprojects = []\\nfor i in os.listdir(basepath):\\n    if i == 'preview':\\n        continue\\n    if os.path.isdir(basepath + i):\\n        if os.path.isfile(basepath + i + '/status'):\\n            with open(basepath + i + '/status', 'r') as file:\\n                status = file.read().strip('\\\\n')\\n                if status in ['expired', 'processing done', 'processing failed']:\\n                    continue\\n        projects.append(i)\\n\\nfor p in projects:\\n    r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\\n    if r.status_code == 200:\\n        answer = r.json()\\n        if answer == {}:\\n            os.system('rm -r ' + basepath + p)\\n        else:\\n            with open(basepath + p + '/status', 'w+') as file:\\n                file.write(answer['status'])\\n            with open(basepath + p + '/download', 'w+') as file:\\n                file.write(answer['dlink'])\\n\\nmsg['list'] = projects\\nsleep(0.5)\\nsave('status_cloud','ready')\\nreturn msg, None\\n\",\n        \"outputs\": 2,\n        \"x\": 320,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\",\n                \"b42e061fb1f1f3d7\"\n            ],\n            [\n                \"6434e713f088012b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"372e95797a3f2f3b\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"limit :)\",\n        \"func\": \"from time import sleep\\n\\nmsg2={}\\nmsg2['enabled'] = True\\n\\nmsg['enabled'] = False\\nnode.send(msg)\\n\\nwait = 15\\n\\nfor i in range (wait):\\n    msg['text'] = ' ('+ str(wait - i)+')'\\n    node.send(msg)\\n\\nmsg['enabled'] = True\\nmsg['text']=\\\"\\\"\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 90,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"573edbfdb7500ddc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573edbfdb7500ddc\",\n        \"type\": \"delay\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dacb1f078b624e10\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"c8d65cc7c2ff7c36\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92c98e6ce7cd25f9\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"links\": [\n            \"7a93d1e18254685c\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 35,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3d16b3789632784d\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 770,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6434e713f088012b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 470,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8d65cc7c2ff7c36\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\nfor i in os.listdir(dir):\\n    if os.path.isdir(dir + i):\\n        shutil.rmtree(dir + i)\\n    else:\\n        os.remove(dir + i)\\n\\nos.mkdir(dir+'/preview')\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4e9a4bd79b4221f\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dacb1f078b624e10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2806bf08ea21216d\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"msg\",\n        \"func\": \"msg.Set=global.get('set')['Set']\\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"15de0ebb.616d61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"61990987acd0f263\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 45,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"51579603bce21e98\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e8e488a6dd5d0b33\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"Download\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href={{msg.download}}>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 880,\n        \"y\": 260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0c387c0291d6c131\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"msg\",\n        \"func\": \"msg.download = '/scans/' + String(msg.payload.Set)\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"e8e488a6dd5d0b33\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e5f38b4a07a5e278\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 655,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"834046a4.647938\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e434ef42bd6b92e8\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"upload2\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">upload</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f6bd1a04.470838\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c46e10b9c201913e\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"refresh\",\n        \"order\": 2,\n        \"width\": 4,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">refresh{{msg.text}}</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 160,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"372e95797a3f2f3b\",\n                \"4d99c601c9881680\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d5d840183025d91b\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del set\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;color:red; width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"2806bf08ea21216d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab9e90ab5a53a0dd\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del \",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete all</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"f4e9a4bd79b4221f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"478994f671a3907d\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"combine\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">combine</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"51bfd0fb7b1d292e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"189c1eed09624a7b\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"combine\",\n        \"func\": \"combine = global.get('combine')\\ncombine_set = global.get('set').Set\\n\\nif (combine === true && global.get('combine_set') !== combine_set){\\n    msg.set1 = global.get('combine_set')\\n    msg.set2 = combine_set\\n    global.set('combine', false)\\n    msg.topic = 'Combine the following two sets:'\\n    msg.payload = msg.set1 + '<br>' + msg.set2 + '<br>FILES WILL BE MERGED INTO ON FILE!'\\n    return msg\\n}\\nglobal.set('combine_set' , combine_set)\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"1493398979a63775\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51bfd0fb7b1d292e\",\n        \"type\": \"function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"combine\",\n        \"func\": \"global.set('combine', true)\\ncombine_set = global.get('set').Set\\nmsg.topic = 'Merge two sets into one (can not be undone)!'\\nmsg.payload = combine_set\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 420,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"da325be8e74179be\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"combine\",\n        \"func\": \"from os.path import getsize\\nfrom shutil import copy\\nfrom os import rename, remove\\nimport zipfile as z\\nfrom OpenScan import save\\n\\nfrom time import sleep\\n\\nif msg['payload'] != 'OK':\\n    return\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\ntmp1 = basepath + msg['set1']\\ntmp2 = basepath + msg['set2']\\n\\nif getsize(tmp1) > getsize(tmp2):\\n    set1 = tmp1\\n    set2 = tmp2\\nelse:\\n    set1 = tmp2\\n    set2 = tmp1\\n\\nzips = [set1, set2]\\n\\nwith z.ZipFile(set1, 'a') as z1:\\n    z2 = z.ZipFile(set2, 'r')\\n    i = 0\\n    for n in z2.namelist():\\n        i += 1\\n        n2 = n\\n        save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\\n        while 'X'+n in z1.namelist():\\n            n = 'X' + n\\n        z1.writestr('X'+n, z2.open(n2).read())\\nsave('status_cloud','ready')\\n\\nos.rename(set1, set1[:-4] + 'X.zip')\\nos.remove(set2)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 560,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"ed35109311335099\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ed35109311335099\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"2f4c0f98.dee2\"\n        ],\n        \"x\": 655,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1493398979a63775\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Combine\",\n        \"x\": 420,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"da325be8e74179be\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ada1b6f7cccc9344\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"combine\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"6dd356510c446cf4\"\n        ],\n        \"x\": 835,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6dd356510c446cf4\",\n        \"type\": \"link in\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"combine\",\n        \"links\": [\n            \"ada1b6f7cccc9344\"\n        ],\n        \"x\": 175,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"189c1eed09624a7b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b42e061fb1f1f3d7\",\n        \"type\": \"link out\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"397ab7f44b893c89\",\n            \"3876d5cbd248592b\"\n        ],\n        \"x\": 435,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b99505440832439f\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"diskspace\",\n        \"func\": \"from subprocess import getoutput\\nfrom OpenScan import load_int\\n\\ndiskspace_threshold = load_int('diskspace_threshold')\\n\\ndiskspace = getoutput('df -h / | awk \\\"{print $5}\\\"').split('\\\\n')[1]\\n\\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\\n\\n\\nif available < diskspace_threshold:\\n    msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \\n    msg['payload'] = 'Please delete some/all locally stored files.'\\n    msg['color'] = 'red'\\n    return msg\\n\",\n        \"outputs\": 1,\n        \"x\": 800,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"92047434f8e9f927\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92047434f8e9f927\",\n        \"type\": \"ui_toast\",\n        \"z\": \"80a3942785a26c29\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 950,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f3662f8c7d3d7a2d\",\n        \"type\": \"delay\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"minute\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 650,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"b99505440832439f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51579603bce21e98\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"read\",\n        \"func\": \"from OpenScan import load_str\\nfrom os import listdir, path\\n\\nstatus = load_str('status_cloud')\\n\\nif status[0:9] == 'uploading':\\n    progress = load_str('status_uploadprogress')[-6:]\\n    if progress[-1:] == '%':\\n        status = status + ' (' + progress + ')'\\n\\nif status[0:7] == 'zipping':\\n    path1 = '/home/pi/OpenScan/tmp/split/'\\n    files = listdir(path1)\\n    size1 = 0\\n    for file in files:\\n        size1 += path.getsize(path1+file)\\n    size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\\n    \\n    status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\\n\\nmsg['status'] = status\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 130,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"952ce286.4ffd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a5baae623355f9d\",\n        \"type\": \"ui_template\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"preview\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 6,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 301px;max-height: 301px\\\"/>\\n\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 1020,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"85839a17fb7b58b9\",\n        \"type\": \"python3-function\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"preview\",\n        \"func\": \"from time import time\\nimport os\\n\\npath = '/home/pi/OpenScan/scans/preview/'\\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\\n\\nmsg['payload']=\\\"/scans/preview/\\\" + image +\\\"?ts=\\\"+str(int(time()*10))\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 880,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"9a5baae623355f9d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50020809fbd23673\",\n        \"type\": \"inject\",\n        \"z\": \"80a3942785a26c29\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"5\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 310,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"01e4783e148c6698\",\n        \"type\": \"ui_table\",\n        \"z\": \"80a3942785a26c29\",\n        \"group\": \"b5fdd57b.15eda8\",\n        \"name\": \"\",\n        \"order\": 1,\n        \"width\": 13,\n        \"height\": 7,\n        \"columns\": [\n            {\n                \"field\": \"Date\",\n                \"title\": \"Date\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Name\",\n                \"title\": \"Name\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Photos\",\n                \"title\": \"Photos\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Duration\",\n                \"title\": \"ΔT\",\n                \"width\": \"60\",\n                \"align\": \"left\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Size\",\n                \"title\": \"Size\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Status\",\n                \"title\": \"Status\",\n                \"width\": \"140\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            }\n        ],\n        \"outputs\": 1,\n        \"cts\": true,\n        \"x\": 610,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"4082b136.dae18\",\n                \"50710948.71c308\",\n                \"834046a4.647938\",\n                \"0c387c0291d6c131\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb3437ec113e1b6f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"SSH\",\n        \"tooltip\": \"\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"c24f61b87e3226dd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"60fd0adce1cfeb82\",\n        \"type\": \"ui_switch\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"Samba\",\n        \"tooltip\": \"\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"test2\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"441d3ef525e901da\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c24f61b87e3226dd\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"ssh\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('ssh'):\\n    save('ssh', state)\\n\\nif state == True:\\n    os.system('/etc/init.d/ssh start')\\nelse:\\n    os.system('/etc/init.d/ssh stop')\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c013e836e759a085\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Terms Of Use\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 120,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"b78346ca3ce70c68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f0d8dbcca76a1926\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Agree\",\n        \"cancel\": \"Disagree\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 410,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"e95b86cbac1b03b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"34374044c0030625\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"General\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>General Settings</h1> <p><strong>Terms Of Use</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.</p> <p><strong>SSH</strong></p> <p style=\\\"margin-left: 20px;\\\">SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.</p> <p style=\\\"margin-left: 20px;\\\">If you want to use it, the default user is <strong>pi</strong>, password: <strong>raspberry. Please change the password&nbsp;</strong><strong>immediately.</strong>&nbsp;</p> <p><strong>Samba</strong></p> <p style=\\\"margin-left: 20px;\\\">Samba s a network local file sharing server, which allows accessing the Raspberry Pi&apos;s file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">You can access the Raspberry Pis file system by inserting the following line into your Windows explorer:&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">\\\\\\\\OpenScan/PiShare/OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">username: pi, password: raspberry</p> <p style=\\\"margin-left: 20px;\\\"><strong>Please deactivate the local file sharing if you do not intend to use it</strong></p> <p><strong>Advanced Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">Enable a ton of additional settings, which should be changed only if you know what you are doing ;)</p> <p><strong>Model</strong></p> <p style=\\\"margin-left: 20px;\\\">Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.</p> <p><strong>Camera</strong></p> <p style=\\\"margin-left: 20px;\\\">A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.</p> <p style=\\\"margin-left: 20px;\\\"><u>DSLR (gphoto)</u> - connect a wide range of DSLR cameras to the device through USB. See <a href=\\\"http://www.gphoto.org/proj/libgphoto2/support.php\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">GPhoto</a> for a full list of supported devices.</p> <p style=\\\"margin-left: 20px;\\\"><u>External camera</u> - triggering any camera through an isolated GPIO signal on the front side of the pi shield.</p> <p><strong>Shutdown/Reboot</strong></p> <p style=\\\"margin-left: 20px;\\\">Always use the shutdown button before you power off your Raspberry Pi.</p> <p><strong>Restore Default Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">In case you want to restore the default settings</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 740,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2b6bf23c9989133\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Pinout\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1><span style=\\\"color: rgb(226, 80, 65);\\\">Pinout</span></h1> <p><span style=\\\"color: rgb(226, 80, 65);\\\"><strong><span style=\\\"font-size: 19px;\\\">ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!</span></strong></span></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"441d3ef525e901da\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"smb\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('smb'):\\n    save('smb', state)\\nif state == True:\\n    os.system('/etc/init.d/smbd start')\\nelse:\\n    os.system('/etc/init.d/smbd stop')\\n\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"3256bab150113a48\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Motor\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Motor Settings</h1> <p><strong>Turntable Mode</strong></p> <p style=\\\"margin-left: 20px;\\\">Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.</p> <p><strong>Rotor - Start Angle, Min and Max Angle</strong></p> <p style=\\\"margin-left: 20px;\\\">Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0&deg; corresponds to the horizontal (natural) orientation.</p> <p style=\\\"margin-left: 20px;\\\">After that, the device will equally space the image positions between angle min and angle max.</p> <p><strong>Rotor/Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>Steps per rotation</u> - &nbsp;defines the number of steps it takes to move the axis 360&deg;. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)</p> <p style=\\\"margin-left: 20px;\\\"><u>Delay</u> - time in microseconds between each step of the motor. Lower this value if the movement is too fast</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration</u> - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration ramp</u> - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.</p> <p style=\\\"margin-left: 20px;\\\"><u>Manual Angle</u> - Defines the degree value for the manual movement through the arrow buttons in the scan menu</p> <p style=\\\"margin-left: 20px;\\\"><u>Direction</u> - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180&deg; (BUT MAKE SURE TO POWER OFF THE DEVICE!)</p> <p style=\\\"margin-left: 20px;\\\"><a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\"></a></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7a186669a17daa71\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"camera\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Camera Settings</h1> <p><strong>Jpeg quality</strong></p> <p style=\\\"margin-left: 20px;\\\">Value in percent, which usually does not need to be changed.</p> <p><strong>Downscale Preview</strong></p> <p style=\\\"margin-left: 20px;\\\">The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update</p> <p><strong>Image Rotation</strong></p> <p style=\\\"margin-left: 20px;\\\">Change the image rotation, if needed.</p> <p><strong>Timeout</strong></p> <p style=\\\"margin-left: 20px;\\\">Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.</p> <p><strong>Delay Before/After</strong></p> <p style=\\\"margin-left: 20px;\\\">A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.</p> <p><strong>AWBG, Gain, Contrast, Saturation</strong></p> <p style=\\\"margin-left: 20px;\\\">Under most circumstances, you do not need to touch these values.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"edac7dd292e7e486\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"General Settings\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"161b52034e578ee2\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Network\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f6d6cc35679ede63\",\n        \"type\": \"ui_switch\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"more sets\",\n        \"label\": \"Advanced Settings\",\n        \"tooltip\": \"\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f06a7bcad524e9f9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"29745a36fc157f3f\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"more sets\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    msg['payload'] = False\\n    return None,msg\\n    \\nsave('advanced_settings', True)\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 820,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"8750ad979e9ea246\"\n            ],\n            [\n                \"f6d6cc35679ede63\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bf23328f9fb11b22\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"change visibility\",\n        \"events\": \"all\",\n        \"x\": 600,\n        \"y\": 60,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"b37be1d222bc70c9\",\n        \"type\": \"inject\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"1s_repeater\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"2\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 150,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"89eedf29b404f750\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"89eedf29b404f750\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"load advanced\",\n        \"func\": \"from OpenScan import load_bool\\n\\nif load_bool('advanced_settings') == False:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\"]}}\\nelse:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\",\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",]}}\\n\\nupdate = load_bool('updateable')\\n\\nmsg2 = {}\\n\\nif update == True:\\n    msg2['payload'] = {\\\"group\\\":{\\\"show\\\":[\\\"OpenScan_Update\\\"]}}\\nelif update == False:\\n    msg2['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"OpenScan_Update\\\"]}}\\n\\n\\nreturn msg,msg2\",\n        \"outputs\": 2,\n        \"x\": 360,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"bf23328f9fb11b22\"\n            ],\n            [\n                \"bf23328f9fb11b22\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2050de5d9e02f69f\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Info Texts\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ded3086945a6d4b5\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"check ip address\",\n        \"func\": \"import socket\\nimport subprocess\\n\\ntestIP = \\\"8.8.8.8\\\"\\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\\ns.connect((testIP, 0))\\nipaddr = s.getsockname()[0]\\nhost = socket.gethostname()\\n\\nmsg['ip']=ipaddr\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 250,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"3cfe464506f46ecd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3cfe464506f46ecd\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Your local IP:\",\n        \"format\": \"{{msg.ip}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bd206ad109831e6a\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"OpenScanCloud\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 1260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b70a9a665c1e4d36\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Cloud-settings\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>OpenScanCloud</h1> <p style=\\\"margin-left: 20px;\\\">OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at <a href=\\\"%5C%22https://www.buymeacoffee.com/OpenScan%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>BuyMeACoffee</a>.</p> <p style=\\\"margin-left: 20px;\\\">The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.</p> <p><strong>Register</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.</p> <p><strong>Enter Token</strong></p> <p style=\\\"margin-left: 20px;\\\">Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Token</strong></p> <p style=\\\"margin-left: 20px;\\\">A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Credit (GB)</strong></p> <p style=\\\"margin-left: 20px;\\\">Each token comes with a given amount of &apos;credit&apos; which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers.&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">IMPORTANT: The credit can be increased at any time by sending a (nice) mail to <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 580,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c9f0566601a3e130\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Number of Photos:\",\n        \"format\": \"{{msg.limit_photos}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9bd86d27ea499a2a\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Filesize (GB):\",\n        \"format\": \"{{msg.limit_filesize}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 1440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2c37f7030810d234\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Credit (GB):\",\n        \"format\": \"{{msg.credit}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 1480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f40286c18afd4501\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"save\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import save, OpenScanCloud\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return None,msg\\n\\ntry:\\n    r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\\n    if r.status_code != 200:\\n        msg['payload'] = 'Could not verify token'\\n        return msg \\n    \\n    msg1 = r.json()\\n    \\n    save('osc_credit',msg1['credit'])\\n    save('osc_limit_filesize',msg1['limit_filesize'])\\n    save('osc_limit_photos',msg1['limit_photos'])\\n    msg1['enabled'] = True\\nexcept:\\n    pass\\n\\nsave('token',msg['token'])\\n    \\nmsg['payload'] = 'Token verified and saved'\\nreturn msg, msg1\",\n        \"outputs\": 2,\n        \"x\": 750,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"455a5266017ea121\",\n                \"50f73cee213ec05c\"\n            ],\n            [\n                \"264eece408043021\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"455a5266017ea121\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 890,\n        \"y\": 1300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c368df68593bc2bf\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"Token\",\n        \"tooltip\": \"\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 350,\n        \"y\": 1360,\n        \"wires\": [\n            [\n                \"18fd1afa768187b3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"18fd1afa768187b3\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Save?\",\n        \"func\": \"msg['token'] = msg['payload']\\n\\nif len(msg['payload'])>=14:\\n    \\n    msg[\\\"payload\\\"]='Save and verify token: ' + msg['payload']\\n    return msg\\nelse:\\n    return None,msg\",\n        \"outputs\": 2,\n        \"x\": 470,\n        \"y\": 1360,\n        \"wires\": [\n            [\n                \"418aea2ec65573a0\"\n            ],\n            [\n                \"9792c89c5f4429f9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f90a98899b7a71d0\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"text\",\n        \"func\": \"from OpenScan import load_str\\n\\ntoken = load_str('token')[0:8]\\nmsg['payload']= token + '...'\\nif len(token)==0:\\n    msg['payload']=\\\"enter token\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1360,\n        \"wires\": [\n            [\n                \"c368df68593bc2bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b4c843620c251c43\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"token\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50f73cee213ec05c\",\n            \"9792c89c5f4429f9\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 75,\n        \"y\": 1360,\n        \"wires\": [\n            [\n                \"f90a98899b7a71d0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"418aea2ec65573a0\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 610,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"f40286c18afd4501\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9792c89c5f4429f9\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"b4c843620c251c43\"\n        ],\n        \"x\": 555,\n        \"y\": 1380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"264eece408043021\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"links\": [\n            \"5d267acc10020091\",\n            \"3876d5cbd248592b\"\n        ],\n        \"x\": 835,\n        \"y\": 1380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"3876d5cbd248592b\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"OSCparameters\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"264eece408043021\",\n            \"b42e061fb1f1f3d7\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 75,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"5daca3ec47f8e7fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50f73cee213ec05c\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"links\": [\n            \"b4c843620c251c43\",\n            \"5d267acc10020091\"\n        ],\n        \"x\": 835,\n        \"y\": 1340,\n        \"wires\": []\n    },\n    {\n        \"id\": \"95578e54a9b61cba\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 250,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"d7a5693da7855da8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d7a5693da7855da8\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"import re\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmail = msg['payload']\\nemail_regex = re.compile(r\\\"[^@]+@[^@]+\\\\.[^@]+\\\")\\n\\nif email_regex.match(mail) != None:\\n    msg['mail'] = mail\\n    msg['topic'] = 'OpenScanCloud Registration (2/3)'\\n    msg['payload'] = 'Enter your first name'\\n    return msg\\nmsg['payload'] = 'invalid input'\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 390,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"2b02b97dd1614e52\"\n            ],\n            [\n                \"183a629accb417b1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"183a629accb417b1\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2b02b97dd1614e52\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"3e4c15d7b538f816\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3bf622f344172721\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"SUBMIT\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 810,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"e431cb2b8d217cee\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e431cb2b8d217cee\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import OpenScanCloud\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['lastname'] = msg['payload']\\n\\nmsg2 = {}\\n\\nfor i in ['forename','lastname','mail']:\\n    msg2[i] = msg[i]\\n\\nr = OpenScanCloud('requestToken',msg2)\\n\\nstatus = r.status_code\\n\\nmsg['topic'] = 'OpenScanCloud Registration - Success'\\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\\n\\nif status != 200:\\n    msg['topic'] = 'OpenScanCloud Registration - Failed'\\n    msg['payload'] = 'Registration failed, please try again.'\\n\\nmsg['status'] = status\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 950,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"106874534890f229\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a38d7fde5c73210f\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Register\",\n        \"group\": \"12b719cba49817c9\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Register\",\n        \"tooltip\": \"testtesttest\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Please enter your email address:\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Requesting an OpenScanCloud Token\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"95578e54a9b61cba\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"106874534890f229\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1090,\n        \"y\": 1540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"5daca3ec47f8e7fc\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import load_int\\n\\nmsg = {}\\n\\ntry:\\n    msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\\n    msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\\n    msg['limit_photos'] = load_int('osc_limit_photos')\\n    return msg\\nexcept:\\n    pass\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"c9f0566601a3e130\",\n                \"9bd86d27ea499a2a\",\n                \"2c37f7030810d234\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f34de19d4cf810a9\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Motor\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 1740,\n        \"wires\": []\n    },\n    {\n        \"id\": \"26c2b58e21f97475\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Camera\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 2500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a8ec972bad47a9a8\",\n        \"type\": \"comment\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Pinout\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 2960,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b03e8b51187e88eb\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Rotor_delay (ms)\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2100,\n        \"wires\": [\n            [\n                \"11fd3363416433f9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6aae9d4fddf08cc0\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt delay\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 30,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2340,\n        \"wires\": [\n            [\n                \"e50492d1e18f43c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"543e1690693acbeb\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2140,\n        \"wires\": [\n            [\n                \"e8b24efb0f30288e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a56c087d941f1da\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"100\",\n        \"max\": \"5000\",\n        \"step\": \"100\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 2180,\n        \"wires\": [\n            [\n                \"29f576be9e292232\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dfdebe10dbf0e198\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 460,\n        \"y\": 2060,\n        \"wires\": [\n            [\n                \"78e256083f59f66f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"af8dfe78cbd0c301\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Accramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 2140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ee4b8908a5b83880\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_Steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 810,\n        \"y\": 2180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c4deaa38c1b0adbf\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"baec873a95fff48a\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2060,\n        \"wires\": []\n    },\n    {\n        \"id\": \"355e89ab4e5484e4\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 26,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"tt\",\n        \"label\": \"TURNTABLE\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 2300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"10687d331a732790\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 32,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2380,\n        \"wires\": [\n            [\n                \"af88b9da72917d62\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"721b9680a3fa460e\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 34,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"500\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"b1b4678827d3a6dd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6642c7470d3820c\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 28,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 450,\n        \"y\": 2300,\n        \"wires\": [\n            [\n                \"eef89545ec0f6aa8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"18e5918748660109\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 33,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ttAccramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8e805244dc1899e8\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 27,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 800,\n        \"y\": 2300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a09e5fbea861bfb1\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 31,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7b06448b3b222011\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 29,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2340,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0dfc86d90258f9bb\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 22,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2220,\n        \"wires\": [\n            [\n                \"c4b5a38c5c1df3d2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9319d7d4f34c6d22\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 21,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1610895f430b9aca\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 36,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"0f3367983bb8e159\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"96a9febc0928b6f0\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 35,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e2c5ea8c16a5ea32\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"rotor\",\n        \"label\": \"ROTOR\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 1820,\n        \"wires\": []\n    },\n    {\n        \"id\": \"277037c4716d85bf\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"tt_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 38,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"c9d2e31514def4fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1361134e9847f003\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 24,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2260,\n        \"wires\": [\n            [\n                \"523717b0f218a5fd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6b0d58943ecb8bb2\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 37,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"08f93dd2aeedb391\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 23,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"46b91bef44714366\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"advanced settings\",\n        \"links\": [\n            \"8750ad979e9ea246\"\n        ],\n        \"x\": 95,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"89eedf29b404f750\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8750ad979e9ea246\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"46b91bef44714366\"\n        ],\n        \"x\": 955,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2522f888dc58972f\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_delay_before\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"0.02\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2600,\n        \"wires\": [\n            [\n                \"5c752757090c49d2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"30e8df3d616512d8\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_gain\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 2640,\n        \"wires\": [\n            [\n                \"a1769f0277834f6d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d855d926df89d65b\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_contrast\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2760,\n        \"wires\": [\n            [\n                \"1a8b0ba21b4f3005\",\n                \"654bc70a18820828\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7617517dc8ba2859\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_saturation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2800,\n        \"wires\": [\n            [\n                \"dc8fc962ff7d594b\",\n                \"e64feb03a791ca33\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cbaa23c34e10fae1\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_jpeg_q\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"100\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2840,\n        \"wires\": [\n            [\n                \"00e7836ccb3c4d0c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bbe443b039a14e21\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_before\",\n        \"label\": \"Delay before\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d320ed3d701e6cc2\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"gain\",\n        \"label\": \"Gain\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 2640,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f5834dd4646c8af9\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"contrast\",\n        \"label\": \"Contrast\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2760,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ae9a4e19469813ef\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"saturation\",\n        \"label\": \"Saturation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bd629d0d31233c8b\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"jpegQ\",\n        \"label\": \"Jpeg Quality\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 2840,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e89f61dbe6a6cffe\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"ext\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 3,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 390,\n        \"y\": 3000,\n        \"wires\": [\n            [\n                \"885bc559fafec5f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ece38cb172a12d75\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 2,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ext\",\n        \"label\": \"External Camera\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 3000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"70014da0b6ab6698\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"light1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 390,\n        \"y\": 3040,\n        \"wires\": [\n            [\n                \"f70321c96bf81360\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"29634ea5f6d666df\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 4,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light1\",\n        \"label\": \"Light 1\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 3040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2544963852c6881a\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"light2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 390,\n        \"y\": 3080,\n        \"wires\": [\n            [\n                \"95e1603bbd06a69d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"27903533cd85a59e\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 6,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light2\",\n        \"label\": \"Light 2\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 3080,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a1394401246eb735\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotordir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 400,\n        \"y\": 3120,\n        \"wires\": [\n            [\n                \"a8f92ea6bf394640\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bc0aa4bacdfa94ea\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 8,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotordir\",\n        \"label\": \"Rotor direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 3120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f15ca4518b5f223e\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotorstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 400,\n        \"y\": 3160,\n        \"wires\": [\n            [\n                \"06397bb46b3bb541\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0d2924b160e7e383\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 10,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotorstep\",\n        \"label\": \"Rotor step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 3160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"49900bb9047dd965\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotoren\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 13,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 400,\n        \"y\": 3200,\n        \"wires\": [\n            [\n                \"687dcdc1ede11700\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4d743ca73ee1622\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 12,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotoren\",\n        \"label\": \"Rotor enable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 3200,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5a90224dc998b417\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"ttdir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 15,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 390,\n        \"y\": 3240,\n        \"wires\": [\n            [\n                \"e220740c0d38ccb0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67dc1b544c4ddf9f\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 14,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttdir\",\n        \"label\": \"Turntable direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 3240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d2364ab09627fe94\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"ttstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 17,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 390,\n        \"y\": 3280,\n        \"wires\": [\n            [\n                \"79d7e5a705ab813a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"145b67ac40721ba6\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 16,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttstep\",\n        \"label\": \"Turntable step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 3280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"eef25405472acfee\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"endstop1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 19,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 400,\n        \"y\": 3320,\n        \"wires\": [\n            [\n                \"12d20f2274bcc511\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"35eb252a41413531\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 18,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop1\",\n        \"label\": \"Endstop Rotor\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 3320,\n        \"wires\": []\n    },\n    {\n        \"id\": \"74e455136b5ca5dd\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"endstop2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 21,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 400,\n        \"y\": 3360,\n        \"wires\": [\n            [\n                \"a4a89668ce4c9f05\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3a74f653800eb831\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"70d0be671bf03ca7\",\n        \"order\": 20,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop2\",\n        \"label\": \"Endstop Turntable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 740,\n        \"y\": 3360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5fcef1cb2e9e4788\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"confirm\",\n        \"x\": 680,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"29745a36fc157f3f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f06a7bcad524e9f9\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import save, load_bool\\n\\nif msg['payload'] == True and not load_bool('advanced_settings'):\\n    msg['payload'] = '''<p style=\\\"text-align: center;\\\"><span style=\\\"font-size: 22px;\\\"><strong><u>PLEASE READ :)</u></strong></span></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Modifying the advanced settings can potentially damage your device and/or the connected peripherals.</strong></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Please read the given information texts carefully and only change settings, when you are sure about the consequences!</strong></p>\\n'''\\n    return msg\\nelif not msg['payload']: \\n    save('advanced_settings', False)\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"5fcef1cb2e9e4788\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f455fb39039617ae\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_rotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"270\",\n        \"step\": \"90\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2880,\n        \"wires\": [\n            [\n                \"3019576de193d9d6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fdfbc900fe424eb9\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cam_rot\",\n        \"label\": \"Image Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c3699d6b9664ccca\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2060,\n        \"wires\": [\n            [\n                \"dfdebe10dbf0e198\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"78e256083f59f66f\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2060,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0f9141b401322374\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2180,\n        \"wires\": [\n            [\n                \"9a56c087d941f1da\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"29f576be9e292232\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2180,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"23e3099b34c4e475\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2220,\n        \"wires\": [\n            [\n                \"0dfc86d90258f9bb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c4b5a38c5c1df3d2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"79a14162ac805fac\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2260,\n        \"wires\": [\n            [\n                \"1361134e9847f003\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"523717b0f218a5fd\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f5cf780f3fa8997e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2100,\n        \"wires\": [\n            [\n                \"b03e8b51187e88eb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"11fd3363416433f9\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"02060b3f3b294563\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2140,\n        \"wires\": [\n            [\n                \"543e1690693acbeb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e8b24efb0f30288e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"de1ad8b27b72a5ac\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nsteps = parseInt(data);\\nif (steps == 3600){\\n    steps = 3200\\n}\\n\\nmsg.payload = steps\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2300,\n        \"wires\": [\n            [\n                \"c6642c7470d3820c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ed4d587cb4feb064\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"721b9680a3fa460e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5b02160c33605ae7\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"1610895f430b9aca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"304c135ec09801e3\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"277037c4716d85bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a91dcbe0f9a2416a\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2340,\n        \"wires\": [\n            [\n                \"6aae9d4fddf08cc0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6b2eb1cb95e573f9\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2380,\n        \"wires\": [\n            [\n                \"10687d331a732790\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"eef89545ec0f6aa8\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"b1b4678827d3a6dd\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2420,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0f3367983bb8e159\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c9d2e31514def4fc\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e50492d1e18f43c6\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"af88b9da72917d62\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2380,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"43fe948b3e7234e2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2600,\n        \"wires\": [\n            [\n                \"2522f888dc58972f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c752757090c49d2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2600,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"435681b3f7625a7e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2640,\n        \"wires\": [\n            [\n                \"30e8df3d616512d8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1769f0277834f6d\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1de07c7d285cbaf3\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2760,\n        \"wires\": [\n            [\n                \"d855d926df89d65b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1a8b0ba21b4f3005\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ebc9e283468eda31\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2800,\n        \"wires\": [\n            [\n                \"7617517dc8ba2859\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dc8fc962ff7d594b\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2800,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"60d641613527c736\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2840,\n        \"wires\": [\n            [\n                \"cbaa23c34e10fae1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"00e7836ccb3c4d0c\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2840,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"7f24c0c34a88ba04\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2880,\n        \"wires\": [\n            [\n                \"f455fb39039617ae\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3019576de193d9d6\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2880,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"77bb7dc529d63a7e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3000,\n        \"wires\": [\n            [\n                \"e89f61dbe6a6cffe\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"885bc559fafec5f2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cc6dabe017a9c8a8\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3320,\n        \"wires\": [\n            [\n                \"eef25405472acfee\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"12d20f2274bcc511\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"dcb9fed8122759fd\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3040,\n        \"wires\": [\n            [\n                \"70014da0b6ab6698\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f70321c96bf81360\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3040,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"013d2057c2347a62\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3080,\n        \"wires\": [\n            [\n                \"2544963852c6881a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"95e1603bbd06a69d\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3080,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f88bbf11d5aa9a14\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3120,\n        \"wires\": [\n            [\n                \"a1394401246eb735\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a8f92ea6bf394640\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"301af70731e096e5\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3160,\n        \"wires\": [\n            [\n                \"f15ca4518b5f223e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"06397bb46b3bb541\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3160,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0456a9ec4c236c9e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3200,\n        \"wires\": [\n            [\n                \"49900bb9047dd965\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"687dcdc1ede11700\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"09d37ba08ec0f163\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3240,\n        \"wires\": [\n            [\n                \"5a90224dc998b417\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"37d954a4cf7e87ea\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3280,\n        \"wires\": [\n            [\n                \"d2364ab09627fe94\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e220740c0d38ccb0\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3240,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"79d7e5a705ab813a\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"21dc963d967d9c99\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 3360,\n        \"wires\": [\n            [\n                \"74e455136b5ca5dd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4a89668ce4c9f05\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 590,\n        \"y\": 3360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"22ef66b0e2058be2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'ssh'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"cb3437ec113e1b6f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ce01c8ba97932c1\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'smb'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"60fd0adce1cfeb82\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"81356177176eebcf\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'advanced_settings'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f6d6cc35679ede63\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b78346ca3ce70c68\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'This is a free piece of software and it is provided as is, without any warranty. <br>There might be functions that need a connection to the internet: '+\\n    '<br><br>By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP  to my servers '+\\n    '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days  The images might be used for further experiments (e.g. machine learning, automation ...). '+\\n    '<br><br>By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\\n    'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\\n    'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\\n    '<br><br>By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\\n    'The raw images and resulting 3d models will never be published without your explicit consent.'+  \\n    '<br><br>If you have any questions you can contact me at info@openscan.eu.'+ \\n    '<br><br>THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\\n    'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \\n    'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \\n    'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"f0d8dbcca76a1926\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e95b86cbac1b03b9\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var data\\n\\nif(msg.payload === 'Agree'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nvar file = 'terms'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nfs.writeFile(filepath+file, String(data), err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"3e4c15d7b538f816\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload === 'Cancel'){\\n    return\\n}\\nmsg.forename = msg.payload\\nmsg.topic = 'OpenScanCloud Registration (3/3)'\\nmsg.payload = 'Enter your last name'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 670,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"3bf622f344172721\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0f0871baf322b6d0\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1820,\n        \"wires\": [\n            [\n                \"6ebd15c61a5ca891\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f21a95a732fadae6\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"Min Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1820,\n        \"wires\": []\n    },\n    {\n        \"id\": \"acd10a4c99ee8063\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1820,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ebd15c61a5ca891\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1820,\n        \"wires\": [\n            [\n                \"acd10a4c99ee8063\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3ad0f0f206e4a873\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1860,\n        \"wires\": [\n            [\n                \"031d7697768d0e77\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3b6d759ed5be647f\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1900,\n        \"wires\": [\n            [\n                \"be1954dd71d2c94c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"edb1c8fae8b65c82\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1860,\n        \"wires\": [\n            [\n                \"3ad0f0f206e4a873\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"031d7697768d0e77\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1860,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"462a8f3ca75fc3c8\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1900,\n        \"wires\": [\n            [\n                \"3b6d759ed5be647f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"be1954dd71d2c94c\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1900,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"3d7379753d2eda25\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"Max Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1860,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9cc86d1bcae3ab4e\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"Start Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2e9b29c70969cf01\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 135,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"22ef66b0e2058be2\",\n                \"9ce01c8ba97932c1\",\n                \"81356177176eebcf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"592ec13d8f8923a9\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"ip address\",\n        \"links\": [\n            \"50eeb3e362f9027f\",\n            \"960912e90ba5b5bc\",\n            \"eb1a2387a1eeea76\",\n            \"c994c779e4bad800\"\n        ],\n        \"x\": 85,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"ded3086945a6d4b5\",\n                \"6ea3cdab41f20f92\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb40b9341bd22a28\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 185,\n        \"y\": 1820,\n        \"wires\": [\n            [\n                \"0f0871baf322b6d0\",\n                \"edb1c8fae8b65c82\",\n                \"462a8f3ca75fc3c8\",\n                \"c3699d6b9664ccca\",\n                \"f5cf780f3fa8997e\",\n                \"02060b3f3b294563\",\n                \"0f9141b401322374\",\n                \"23e3099b34c4e475\",\n                \"79a14162ac805fac\",\n                \"de1ad8b27b72a5ac\",\n                \"a91dcbe0f9a2416a\",\n                \"6b2eb1cb95e573f9\",\n                \"ed4d587cb4feb064\",\n                \"5b02160c33605ae7\",\n                \"304c135ec09801e3\",\n                \"f036424d79645761\",\n                \"b7db72b7f0599ebd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d1efcd5fa9d25785\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 155,\n        \"y\": 2540,\n        \"wires\": [\n            [\n                \"43fe948b3e7234e2\",\n                \"435681b3f7625a7e\",\n                \"1de07c7d285cbaf3\",\n                \"ebc9e283468eda31\",\n                \"60d641613527c736\",\n                \"7f24c0c34a88ba04\",\n                \"6281b2e6e081104d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da61581182b7299e\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 135,\n        \"y\": 3000,\n        \"wires\": [\n            [\n                \"77bb7dc529d63a7e\",\n                \"dcb9fed8122759fd\",\n                \"013d2057c2347a62\",\n                \"f88bbf11d5aa9a14\",\n                \"301af70731e096e5\",\n                \"0456a9ec4c236c9e\",\n                \"09d37ba08ec0f163\",\n                \"37d954a4cf7e87ea\",\n                \"cc6dabe017a9c8a8\",\n                \"21dc963d967d9c99\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7e1c84ec516ad0a6\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Reset default\",\n        \"group\": \"4390b2ebcbbe104c\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Restore default settings\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"This can not be undone!\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Restore default settings?\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"53e6681d7254d484\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"53e6681d7254d484\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 270,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"c11e79cfa7bc10b7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c11e79cfa7bc10b7\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"msg.overwrite = true\\nif(msg.payload == \\\"Yes\\\"){\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"307782d10c1acdaf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"307782d10c1acdaf\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 505,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5fff689f9f8bc1ca\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Info\",\n        \"x\": 1010,\n        \"y\": 140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cca3300a8f0daf4d\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"Update&Info\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Update&amp;Log</h1> <p><strong>Status</strong></p> <p>See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a> for details and the source code.</p> <p><strong>Updatetype</strong></p> <p>- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras</p> <p>- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well</p> <p>- mini: very simplified firmware for the OpenScanMini + Arducam IMX519</p> <p><strong>Auto-Check update availability</strong></p> <p>Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p> <p>This option is activated by default.</p> <p><strong>Check Updates</strong></p> <p>Alternatively, you can check for updates manually at any time by pressing this button.</p> <p><strong>Download Error Log</strong></p> <p>In case you encounter any errors with your device, please download the error log text and send a copy to <a href=\\\"%5C%22mailto:info@openscan.eu%5C%22\\\">info@openscan.eu</a> or create an issue on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 750,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"5fff689f9f8bc1ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"654bc70a18820828\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"func\": \"from OpenScan import camera\\n\\ncamera(\\\"/picam2_contrast?contrast=\\\" + str(msg['payload']))\",\n        \"outputs\": 1,\n        \"x\": 660,\n        \"y\": 2720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e64feb03a791ca33\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"func\": \"from OpenScan import camera\\n\\ncamera(\\\"/picam2_saturation?saturation=\\\" + str(msg['payload']))\",\n        \"outputs\": 1,\n        \"x\": 660,\n        \"y\": 2680,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"81bd4381cd029958\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"cam_delay_after\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"0.02\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 2560,\n        \"wires\": [\n            [\n                \"e612073aded01a8f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0d92559980944ae3\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"d324f0b852c2df0a\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_after\",\n        \"label\": \"Delay after\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6281b2e6e081104d\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 2560,\n        \"wires\": [\n            [\n                \"81bd4381cd029958\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e612073aded01a8f\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 620,\n        \"y\": 2560,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e2411b49791840e0\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"reboot\",\n        \"func\": \"import os\\nfrom time import sleep\\nsleep(1.5)\\nimport RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nos.system('reboot -h')\\n\",\n        \"outputs\": 1,\n        \"x\": 270,\n        \"y\": 520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"01c882fcc51b349c\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"reboot\",\n        \"links\": [\n            \"16c76929f88df841\",\n            \"fe3a855fee9e28c6\",\n            \"09d4a9c756161e10\"\n        ],\n        \"x\": 155,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"e2411b49791840e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e51dd5e5c0f050d6\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"SSID\",\n        \"tooltip\": \"\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"ssid\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 210,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"a7d233f984009e2e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9959649037cb063b\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"Password\",\n        \"tooltip\": \"\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"password\",\n        \"delay\": \"0\",\n        \"topic\": \"password\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 220,\n        \"y\": 1020,\n        \"wires\": [\n            [\n                \"a7d233f984009e2e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1d42cb9a63409283\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"label\": \"Country Code 2\",\n        \"tooltip\": \"\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"country\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 240,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"a7d233f984009e2e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"84ecaafd629c0f7a\",\n        \"type\": \"ui_button\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 7,\n        \"width\": 0,\n        \"height\": 0,\n        \"passthru\": false,\n        \"label\": \"Connect to Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"connect\",\n        \"topicType\": \"str\",\n        \"x\": 240,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"a7d233f984009e2e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6ea3cdab41f20f92\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"8ab79a98e536e0d6\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Hotspot Mode\",\n        \"format\": \"{{msg.mode}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 240,\n        \"y\": 900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a7d233f984009e2e\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"function 1\",\n        \"func\": \"if (msg.topic == \\\"ssid\\\"){\\n    global.set('network_ssid',msg.payload)\\n}\\nelse if (msg.topic == \\\"password\\\"){\\n    global.set('network_password',msg.payload)\\n}\\nelse if (msg.topic == \\\"country\\\"){\\n    global.set('network_country',msg.payload)\\n}\\nelse if (msg.topic == \\\"connect\\\"){\\n    msg.ssid = global.get('network_ssid')\\n    msg.password = global.get('network_password')\\n    msg.country = global.get('network_country')\\n    msg.payload = \\\"\\\"\\n    return msg\\n}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 440,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"9b851aa999e86fd7\",\n                \"021dc780b478fee6\",\n                \"9ec0ad9fd3687e9f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65518f3d4e3095e5\",\n        \"type\": \"link in\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"link in 1\",\n        \"links\": [\n            \"200d4b9951b6e066\"\n        ],\n        \"x\": 85,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"e51dd5e5c0f050d6\",\n                \"9959649037cb063b\",\n                \"1d42cb9a63409283\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b851aa999e86fd7\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"func\": \"from OpenScan import add_wifi_network, check_hotspot_mode\\nfrom time import sleep\\n\\nsleep(0.5)\\n\\nerror = \\\"\\\"\\nif msg['ssid'] == \\\"\\\":\\n    error = \\\"SSID, \\\"\\nif msg['password'] == \\\"\\\" or len(msg['password'])<8:\\n    error = error + \\\"password, \\\"\\nif msg['country'] == \\\"\\\" or len(msg['country']) != 2:\\n    error = error + \\\"country code\\\"\\n\\nif error != \\\"\\\":\\n    msg['payload'] = error\\n    msg['topic'] = \\\"Invalid Input(s):\\\"\\n    if check_hotspot_mode():\\n        msg['mode'] = True\\n    else:\\n        msg['mode'] = False\\n    return msg\\n\\n\\nmsg['result'] = add_wifi_network(msg['ssid'],msg['password'],msg['country'])\\n\\nsleep(3)\\n\\nif check_hotspot_mode():\\n    msg['mode'] = True\\nelse:\\n    msg['mode'] = False\\n\\nmsg['topic'] = \\\"Added wifi & connected\\\"\\nmsg['payload'] = \\\"changes might take a moment ;)\\\"\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 670,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"c994c779e4bad800\",\n                \"11b19e9c6a4ffd8d\",\n                \"36890eb99a2ca1cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"11b19e9c6a4ffd8d\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 870,\n        \"y\": 980,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"021dc780b478fee6\",\n        \"type\": \"debug\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"debug 3\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"true\",\n        \"targetType\": \"full\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 640,\n        \"y\": 920,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c994c779e4bad800\",\n        \"type\": \"link out\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"link out 2\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"592ec13d8f8923a9\"\n        ],\n        \"x\": 815,\n        \"y\": 1020,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1eef47e0074545a9\",\n        \"type\": \"python3-function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"func\": \"from OpenScan import add_wifi_network, check_hotspot_mode\\n\\nif check_hotspot_mode():\\n    msg['mode'] = True\\nelse:\\n    msg['mode'] = False\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 670,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"c994c779e4bad800\",\n                \"36890eb99a2ca1cf\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"434b04d8a65951ce\",\n        \"type\": \"inject\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 440,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"1eef47e0074545a9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ec0ad9fd3687e9f\",\n        \"type\": \"ui_toast\",\n        \"z\": \"e43a27722b508115\",\n        \"position\": \"bottom right\",\n        \"displayTime\": \"5\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 0,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"Adding new Wifi\",\n        \"name\": \"\",\n        \"x\": 670,\n        \"y\": 1020,\n        \"wires\": []\n    },\n    {\n        \"id\": \"36890eb99a2ca1cf\",\n        \"type\": \"debug\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"debug 4\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"true\",\n        \"targetType\": \"full\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 860,\n        \"y\": 940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6b7245c3dcb694c8\",\n        \"type\": \"ui_slider\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"endstop_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 2020,\n        \"wires\": [\n            [\n                \"85ad07b8f973bbe2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"69516440e3997111\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"endstop_angle\",\n        \"label\": \"Endstop angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 2020,\n        \"wires\": []\n    },\n    {\n        \"id\": \"85ad07b8f973bbe2\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_endstop_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2020,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f036424d79645761\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_endstop_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2020,\n        \"wires\": [\n            [\n                \"6b7245c3dcb694c8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"253feafa5a2f8b1d\",\n        \"type\": \"ui_switch\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"rotor_enable_endstop\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 460,\n        \"y\": 1940,\n        \"wires\": [\n            [\n                \"1916dc3fd04f0664\",\n                \"6cb92b9b9f0d6954\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b7db72b7f0599ebd\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'rotor_enable_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nvar data = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1940,\n        \"wires\": [\n            [\n                \"253feafa5a2f8b1d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1916dc3fd04f0664\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_enable_endstop'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1940,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"de409e57a0c4bf41\",\n        \"type\": \"ui_text\",\n        \"z\": \"e43a27722b508115\",\n        \"group\": \"7a3279eea439bcdd\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_enable_endstop\",\n        \"label\": \"Enable Endstop\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 800,\n        \"y\": 1940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6cb92b9b9f0d6954\",\n        \"type\": \"function\",\n        \"z\": \"e43a27722b508115\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = msg.payload\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 1980,\n        \"wires\": [\n            [\n                \"69516440e3997111\",\n                \"f036424d79645761\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4c7fa5b5b27b83a5\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"create beta new\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\n\\n#scope = 'main'\\nscope = 'betaArdu'\\n\\nupdatepath = '/home/pi/OpenScan/tmp/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\n\\n## load update.json\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'download update.json failed'\\n    return msg\\n\\nmsg = {}\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg2 = msg.copy()\\nif scope in msg:\\n    del msg[scope]\\n\\nmsg[scope]={}\\nmsg[scope]['1'] = {}\\nmsg[scope]['1']['src'] = scope + '/fla.py'\\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\\n\\nmsg[scope]['2'] = {}\\nmsg[scope]['2']['src'] = scope + '/OpenScan.py'\\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\\n\\nmsg[scope]['3'] = {}\\nmsg[scope]['3']['src'] = scope + '/config.txt'\\nmsg[scope]['3']['dst'] = '/boot/config.txt'\\n\\nmsg[scope]['4'] = {}\\nmsg[scope]['4']['src'] = scope + '/flows.json'\\nmsg[scope]['4']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\\n\\nmsg[scope]['5'] = {}\\nmsg[scope]['5']['src'] = scope + '/settings.js'\\nmsg[scope]['5']['dst'] = '/root/.node-red/settings.js'\\n\\n#msg[scope]['6'] = {}\\n#msg[scope]['6']['src'] = 'files/logo.jpg'\\n#msg[scope]['6']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\\n\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    filesize = os.path.getsize(filepath)\\n    msg[scope][i]['filesize'] = filesize\\n\\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\\n    os.system('rm -r /home/pi/OpenScan/tmp/update')    \\nos.makedirs('/home/pi/OpenScan/tmp/update/')\\n\\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\\n    json.dump(msg, f, indent=4)\\n\\nfor i in msg[scope]:\\n    if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\\n        os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\\n    shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\\n\\nmsg['payload'] = 'created with scope: ' + scope\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 260,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"e23c514008cad1a1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"80175eb8dc6ad009\",\n        \"type\": \"inject\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 100,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"4c7fa5b5b27b83a5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d7362e6e0ec7bdaa\",\n        \"type\": \"inject\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"true\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"x\": 90,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"4ce127c61c3c5966\",\n                \"beacc3dc5398fa79\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4ce127c61c3c5966\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"prepare image creation\",\n        \"func\": \"import os\\n\\n#factory reset, reset wpa, create wpa in boot, rm files\\n#should be done before creating a new raspbian image\\n\\nbasepath = '/home/pi/OpenScan/'\\n\\n#remove files\\n\\ndir = basepath + 'scans/'\\n\\nfor i in ['scans/','tmp/']:\\n    os.system('rm -r ' + basepath + i)\\n    os.mkdir(basepath + i)\\n\\n#delete wifi\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('wpa_cli -i wlan0 reconfigure')\\n\\n#create new wpa_supplicant.conf\\nwith open('/boot/wpa_supplicant.conf','w+') as file:\\n    file.write('country=de\\\\nupdate_config=1\\\\nctrl_interface=/var/run/wpa_supplicant\\\\n\\\\nnetwork={\\\\n scan_ssid=1\\\\n ssid=\\\"wlan name\\\"\\\\n psk=\\\"xxxx\\\"\\\\n}')\\nos.system(\\\"chmod a+rwx /boot/wpa_supplicant.conf\\\")\\n\\n\\n#rm tmp dir\\n\\n\\n#stop photos:\\nos.system('systemctl stop flask')\\nos.system('rm -r ' + basepath + 'tmp')\\nos.system('mkdir ' + basepath + 'tmp')\\n\\nos.system('systemctl stop nodered')\\n\\n#reset factory\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"beacc3dc5398fa79\",\n        \"type\": \"link out\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 195,\n        \"y\": 260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e23c514008cad1a1\",\n        \"type\": \"debug\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"debug 1\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"true\",\n        \"targetType\": \"full\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 480,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b0629875a30ae1d7\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"get update\",\n        \"func\": \"import json\\nimport requests\\nfrom OpenScan import load_str\\n\\nif not msg['payload']:\\n    msg['status'] = '--READY--'\\n    return msg\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'no internet connection'\\n    return msg\\n\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg['status'] = 'checking updates'\\n\\nreturn msg, msg\",\n        \"outputs\": 2,\n        \"x\": 390,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\"\n            ],\n            [\n                \"fefe45404bdb19c4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c7b6d05a62172432\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Status:\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 210,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"fefe45404bdb19c4\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"check files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str\\n\\nscope = load_str('update_type')\\nmsg['scope'] = scope\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_old = updatepath + 'update.json'\\n\\ncounter = 0\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    if os.path.isfile(temp):\\n        filesize = os.path.getsize(temp)\\n        msg[scope][i]['filesize2'] = filesize\\n        if filesize == msg[scope][i]['filesize']:\\n            msg[scope][i]['update'] = False\\n            continue\\n    msg[scope][i]['update'] = True\\n\\n    counter += 1\\n\\nif counter == 0:\\n    msg['status'] = 'No new update available'\\nelse:\\n    msg['status'] = 'New update available'\\n    msg['topic'] = msg['status']\\n    msg['payload'] = 'Install & reboot now?'\\n\\nmsg['counter'] = counter\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 550,\n        \"y\": 560,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\",\n                \"ae92a328af306ebb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d0104e0163745993\",\n        \"type\": \"link in\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 115,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"ec30638407332e43\",\n                \"38cbf7965d1c1834\",\n                \"49f1ecb29a3f84f4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ec30638407332e43\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data.replace(/(\\\\r\\\\n|\\\\n|\\\\r)/gm,\\\"\\\"));\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 210,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"2852023f3aa8db10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2852023f3aa8db10\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"stable\",\n                \"value\": \"main\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"beta\",\n                \"value\": \"beta\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"betaArdu\",\n                \"value\": \"betaArdu\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 340,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"1e10b387ee30c486\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1e10b387ee30c486\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 470,\n        \"y\": 480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"274129c51b0b87ef\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 4,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Updatetype: \",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 610,\n        \"y\": 480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"51cd8c8643e6b46a\",\n        \"type\": \"ui_switch\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"label\": \"Auto-check update availability\",\n        \"tooltip\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"1ab4c6b4b232a022\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38cbf7965d1c1834\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 210,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"51cd8c8643e6b46a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ab4c6b4b232a022\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 440,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ae92a328af306ebb\",\n        \"type\": \"ui_toast\",\n        \"z\": \"a5557543ccff5889\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"NO\",\n        \"cancel\": \"YES\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 710,\n        \"y\": 560,\n        \"wires\": [\n            [\n                \"2de63e8e3ae5fb0c\",\n                \"929281fef53e09f8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cbd0afc4aa7b302a\",\n        \"type\": \"link in\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"update status\",\n        \"links\": [\n            \"1bbe2d769f42c313\",\n            \"42061b28cff81f99\"\n        ],\n        \"x\": 115,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"c7b6d05a62172432\",\n                \"c94623ddd9d95f78\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1bbe2d769f42c313\",\n        \"type\": \"link out\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 665,\n        \"y\": 520,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7cf60615d93e696b\",\n        \"type\": \"ui_button\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 7,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Check Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 180,\n        \"y\": 560,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2de63e8e3ae5fb0c\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"download files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str, save\\n\\n\\nscope = msg['scope']\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\n\\nif msg['payload'] != 'YES':\\n    return\\n\\ncounter = 0\\n\\nfor i in msg[scope]:\\n    if msg[scope][i]['update'] == False:\\n        continue\\n    \\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    \\n    r = requests.get(url + msg[scope][i]['src'])\\n    if r.status_code != 200:\\n        msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\\n        return msg\\n    with open(temp, 'wb+') as file:\\n        file.write(r.content)\\n    shutil.copy(temp, msg[scope][i]['dst'])\\n    \\n    if msg[scope][i]['dst'] == '/boot/config.txt':\\n        save('camera','')\\n    \\n    counter += 1\\n\\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\\n\\nif counter == msg['counter']:\\n    updatepath_temp = updatepath + 'update_temp.json'\\n    updatepath_old = updatepath + 'update.json'\\n    shutil.move(updatepath_temp, updatepath_old)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 880,\n        \"y\": 560,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\",\n                \"fe3a855fee9e28c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"929281fef53e09f8\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload == 'YES'){\\n    msg.status = 'Installing updates'\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 850,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"42061b28cff81f99\",\n        \"type\": \"link out\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 995,\n        \"y\": 520,\n        \"wires\": []\n    },\n    {\n        \"id\": \"49f1ecb29a3f84f4\",\n        \"type\": \"function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\n\\nmsg.payload = data\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 210,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fe3a855fee9e28c6\",\n        \"type\": \"link out\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9bb0adbd716ce347\",\n            \"01c882fcc51b349c\"\n        ],\n        \"x\": 995,\n        \"y\": 560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5e7d5e4335d37794\",\n        \"type\": \"link in\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"50eeb3e362f9027f\"\n        ],\n        \"x\": 95,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb5fe78e09fec8a\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"msg\",\n        \"func\": \"\\nfrom subprocess import getoutput\\nimport os\\n\\nmsg['os'] = getoutput(\\\"cat /etc/os-release | grep -i 'PRETTY_NAME'\\\")[13:-1]\\nmsg['device'] = getoutput(\\\"cat /proc/device-tree/model\\\")\\nmsg['flask'] = getoutput(\\\"systemctl status flask |grep -i 'Active:'\\\").split(' ')[6]\\nmsg['osdate'] = getoutput(\\\"vcgencmd version\\\").split('\\\\n')[0]\\nmsg['temp'] = getoutput(\\\"vcgencmd measure_temp\\\").split('=')[1]\\ncpu_total = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $2}'\\\")\\ncpu_used = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $3}'\\\")\\nswap_total = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $2}'\\\")\\nswap_used = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_used = getoutput(\\\"df -h / | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_total = getoutput(\\\"df -h / | tail -n1 |awk '{print $2}'\\\")\\n\\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\\n\\nif msg['flask'] == 'inactive':\\n    os.system('systemctl restart flask')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 210,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"dbc77052ac950624\",\n                \"d97c3068ef5fef96\",\n                \"73a3b828f862312b\",\n                \"901e31453b2bdff8\",\n                \"f983854748ee4763\",\n                \"5347c7c517f5e8c7\",\n                \"3a5016f7003cd72c\",\n                \"6d720c4a4ecd9475\",\n                \"6438b7d060a70d81\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d97c3068ef5fef96\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"OS:\",\n        \"format\": \"{{msg.os}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 490,\n        \"y\": 740,\n        \"wires\": []\n    },\n    {\n        \"id\": \"73a3b828f862312b\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 8,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Flask:\",\n        \"format\": \"{{msg.flask}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 490,\n        \"y\": 780,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dbc77052ac950624\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Device:\",\n        \"format\": \"{{msg.device}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 500,\n        \"y\": 700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"3f42560297fe6978\",\n        \"type\": \"ui_template\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"name\": \"Download LOG\",\n        \"order\": 9,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href='tmp/log.txt'>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download error log\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 180,\n        \"y\": 1060,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c94623ddd9d95f78\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"get update\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['status'] == \\\"No new update available\\\":\\n    save('updateable',False)\\nelif msg['status'] == \\\"New update available\\\":\\n    save('updateable',True)\\n\",\n        \"outputs\": 1,\n        \"x\": 210,\n        \"y\": 360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"39a502b38837273d\",\n        \"type\": \"link in\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"links\": [\n            \"1e7457ea9c2c5e09\"\n        ],\n        \"x\": 245,\n        \"y\": 600,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"901e31453b2bdff8\",\n        \"type\": \"delay\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"10\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 740,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f983854748ee4763\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"{{msg.osdate}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 490,\n        \"y\": 820,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5347c7c517f5e8c7\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU temp:\",\n        \"format\": \"{{msg.temp}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 510,\n        \"y\": 860,\n        \"wires\": []\n    },\n    {\n        \"id\": \"3a5016f7003cd72c\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU memory:\",\n        \"format\": \"{{msg.cpu}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 520,\n        \"y\": 900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6d720c4a4ecd9475\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 6,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Swap memory:\",\n        \"format\": \"{{msg.swap}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 520,\n        \"y\": 940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6438b7d060a70d81\",\n        \"type\": \"ui_text\",\n        \"z\": \"a5557543ccff5889\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 7,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Diskspace:\",\n        \"format\": \"{{msg.diskspace}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 510,\n        \"y\": 980,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8d012912f302be85\",\n        \"type\": \"ui_button\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 8,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Show Details/Changelog\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 210,\n        \"y\": 640,\n        \"wires\": [\n            [\n                \"5242607a723cc628\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5242607a723cc628\",\n        \"type\": \"python3-function\",\n        \"z\": \"a5557543ccff5889\",\n        \"name\": \"Changelog\",\n        \"func\": \"import requests\\n\\ntempfile = '/home/pi/OpenScan/tmp/changelog'\\n\\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\\nr = requests.get(url, allow_redirects=False)\\n\\nwith open(tempfile,'wb') as file:\\n    file.write(r.content)\\n    \\nwith open(tempfile, 'r') as file:\\n    text = file.read()\\n    \\ntext = text.replace('\\\\n','<br>').replace('*', '&emsp; - ')\\nmsg['payload'] = text\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 430,\n        \"y\": 640,\n        \"wires\": [\n            [\n                \"573722197b15bf84\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573722197b15bf84\",\n        \"type\": \"ui_toast\",\n        \"z\": \"a5557543ccff5889\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 610,\n        \"y\": 640,\n        \"wires\": [\n            []\n        ]\n    }\n]"
  },
  {
    "path": "update/betaArdu/settings.js",
    "content": "/**\n * Node-RED Settings created at Thu, 20 Apr 2023 08:41:18 GMT\n *\n * It can contain any valid JavaScript code that will get run when Node-RED\n * is started.\n *\n * Lines that start with // are commented out.\n * Each entry should be separated from the entries above and below by a comma ','\n *\n * For more information about individual settings, refer to the documentation:\n *    https://nodered.org/docs/user-guide/runtime/configuration\n *\n * The settings are split into the following sections:\n *  - Flow File and User Directory Settings\n *  - Security\n *  - Server Settings\n *  - Runtime Settings\n *  - Editor Settings\n *  - Node Settings\n *\n **/\n\nmodule.exports = {\n\n/*******************************************************************************\n * Flow File and User Directory Settings\n *  - flowFile\n *  - credentialSecret\n *  - flowFilePretty\n *  - userDir\n *  - nodesDir\n ******************************************************************************/\n\n    /** The file containing the flows. If not set, defaults to flows_<hostname>.json **/\n    flowFile: \"flows.json\",\n\n    /** By default, credentials are encrypted in storage using a generated key. To\n     * specify your own secret, set the following property.\n     * If you want to disable encryption of credentials, set this property to false.\n     * Note: once you set this property, do not change it - doing so will prevent\n     * node-red from being able to decrypt your existing credentials and they will be\n     * lost.\n     */\n    credentialSecret: false,\n\n    /** By default, the flow JSON will be formatted over multiple lines making\n     * it easier to compare changes when using version control.\n     * To disable pretty-printing of the JSON set the following property to false.\n     */\n    flowFilePretty: true,\n\n    /** By default, all user data is stored in a directory called `.node-red` under\n     * the user's home directory. To use a different location, the following\n     * property can be used\n     */\n    //userDir: '/home/nol/.node-red/',\nuserDir: '/home/pi/OpenScan/settings/.node-red/',\n\n    /** Node-RED scans the `nodes` directory in the userDir to find local node files.\n     * The following property can be used to specify an additional directory to scan.\n     */\n    //nodesDir: '/home/nol/.node-red/nodes',\n\n/*******************************************************************************\n * Security\n *  - adminAuth\n *  - https\n *  - httpsRefreshInterval\n *  - requireHttps\n *  - httpNodeAuth\n *  - httpStaticAuth\n ******************************************************************************/\n\n    /** To password protect the Node-RED editor and admin API, the following\n     * property can be used. See http://nodered.org/docs/security.html for details.\n     */\n    //adminAuth: {\n    //    type: \"credentials\",\n    //    users: [{\n    //        username: \"admin\",\n    //        password: \"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\",\n    //        permissions: \"*\"\n    //    }]\n    //},\n    \n    /** The following property can be used to enable HTTPS\n     * This property can be either an object, containing both a (private) key\n     * and a (public) certificate, or a function that returns such an object.\n     * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener\n     * for details of its contents.\n     */\n\n    /** Option 1: static object */\n    //https: {\n    //  key: require(\"fs\").readFileSync('privkey.pem'),\n    //  cert: require(\"fs\").readFileSync('cert.pem')\n    //},\n\n    /** Option 2: function that returns the HTTP configuration object */\n    // https: function() {\n    //     // This function should return the options object, or a Promise\n    //     // that resolves to the options object\n    //     return {\n    //         key: require(\"fs\").readFileSync('privkey.pem'),\n    //         cert: require(\"fs\").readFileSync('cert.pem')\n    //     }\n    // },\n\n    /** If the `https` setting is a function, the following setting can be used\n     * to set how often, in hours, the function will be called. That can be used\n     * to refresh any certificates.\n     */\n    //httpsRefreshInterval : 12,\n\n    /** The following property can be used to cause insecure HTTP connections to\n     * be redirected to HTTPS.\n     */\n    //requireHttps: true,\n\n    /** To password protect the node-defined HTTP endpoints (httpNodeRoot),\n     * including node-red-dashboard, or the static content (httpStatic), the\n     * following properties can be used.\n     * The `pass` field is a bcrypt hash of the password.\n     * See http://nodered.org/docs/security.html#generating-the-password-hash\n     */\n    //httpNodeAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n    //httpStaticAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n\n/*******************************************************************************\n * Server Settings\n *  - uiPort\n *  - uiHost\n *  - apiMaxLength\n *  - httpServerOptions\n *  - httpAdminRoot\n *  - httpAdminMiddleware\n *  - httpNodeRoot\n *  - httpNodeCors\n *  - httpNodeMiddleware\n *  - httpStatic\n * - httpStaticRoot\n ******************************************************************************/\n\n    /** the tcp port that the Node-RED web server is listening on */\n    uiPort: process.env.PORT || 80,\n\n    /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.\n     * To listen on all IPv6 addresses, set uiHost to \"::\",\n     * The following property can be used to listen on a specific interface. For\n     * example, the following would only allow connections from the local machine.\n     */\n    //uiHost: \"127.0.0.1\",\n\n    /** The maximum size of HTTP request that will be accepted by the runtime api.\n     * Default: 5mb\n     */\n    //apiMaxLength: '5mb',\n\n    /** The following property can be used to pass custom options to the Express.js\n     * server used by Node-RED. For a full list of available options, refer\n     * to http://expressjs.com/en/api.html#app.settings.table\n     */\n    //httpServerOptions: { },\n\n    /** By default, the Node-RED UI is available at http://localhost:1880/\n     * The following property can be used to specify a different root path.\n     * If set to false, this is disabled.\n     */\n    httpAdminRoot: '/editor',\n\n    /** The following property can be used to add a custom middleware function\n     * in front of all admin http routes. For example, to set custom http\n     * headers. It can be a single function or an array of middleware functions.\n     */\n    // httpAdminMiddleware: function(req,res,next) {\n    //    // Set the X-Frame-Options header to limit where the editor\n    //    // can be embedded\n    //    //res.set('X-Frame-Options', 'sameorigin');\n    //    next();\n    // },\n\n\n    /** Some nodes, such as HTTP In, can be used to listen for incoming http requests.\n     * By default, these are served relative to '/'. The following property\n     * can be used to specifiy a different root path. If set to false, this is\n     * disabled.\n     */\n    //httpNodeRoot: '/red-nodes',\n\n    /** The following property can be used to configure cross-origin resource sharing\n     * in the HTTP nodes.\n     * See https://github.com/troygoode/node-cors#configuration-options for\n     * details on its contents. The following is a basic permissive set of options:\n     */\n    //httpNodeCors: {\n    //    origin: \"*\",\n    //    methods: \"GET,PUT,POST,DELETE\"\n    //},\n\n    /** If you need to set an http proxy please set an environment variable\n     * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.\n     * For example - http_proxy=http://myproxy.com:8080\n     * (Setting it here will have no effect)\n     * You may also specify no_proxy (or NO_PROXY) to supply a comma separated\n     * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk\n     */\n\n    /** The following property can be used to add a custom middleware function\n     * in front of all http in nodes. This allows custom authentication to be\n     * applied to all http in nodes, or any other sort of common request processing.\n     * It can be a single function or an array of middleware functions.\n     */\n    //httpNodeMiddleware: function(req,res,next) {\n    //    // Handle/reject the request, or pass it on to the http in node by calling next();\n    //    // Optionally skip our rawBodyParser by setting this to true;\n    //    //req.skipRawBodyParser = true;\n    //    next();\n    //},\n\n    /** When httpAdminRoot is used to move the UI to a different root path, the\n     * following property can be used to identify a directory of static content\n     * that should be served at http://localhost:1880/.\n     * When httpStaticRoot is set differently to httpAdminRoot, there is no need \n     * to move httpAdminRoot\n     */\n    httpStatic: '/home/pi/OpenScan/',\n \n    //httpStatic: '/home/nol/node-red-static/', //single static source\n    /* OR multiple static sources can be created using an array of objects... */\n    //httpStatic: [\n    //    {path: '/home/nol/pics/',    root: \"/img/\"}, \n    //    {path: '/home/nol/reports/', root: \"/doc/\"}, \n    //],\n\n    /**  \n     * All static routes will be appended to httpStaticRoot\n     * e.g. if httpStatic = \"/home/nol/docs\" and  httpStaticRoot = \"/static/\"\n     *      then \"/home/nol/docs\" will be served at \"/static/\"\n     * e.g. if httpStatic = [{path: '/home/nol/pics/', root: \"/img/\"}]\n     *      and httpStaticRoot = \"/static/\"\n     *      then \"/home/nol/pics/\" will be served at \"/static/img/\"\n     */\n    //httpStaticRoot: '/static/',\n\n/*******************************************************************************\n * Runtime Settings\n *  - lang\n *  - logging\n *  - contextStorage\n *  - exportGlobalContextKeys\n *  - externalModules\n ******************************************************************************/\n\n     /** Uncomment the following to run node-red in your preferred language.\n      * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko\n      * Some languages are more complete than others.\n      */\n     // lang: \"de\",\n\n     /** Configure the logging output */\n     logging: {\n         /** Only console logging is currently supported */\n         console: {\n             /** Level of logging to be recorded. Options are:\n              * fatal - only those errors which make the application unusable should be recorded\n              * error - record errors which are deemed fatal for a particular request + fatal errors\n              * warn - record problems which are non fatal + errors + fatal errors\n              * info - record information about the general running of the application + warn + error + fatal errors\n              * debug - record information which is more verbose than info + info + warn + error + fatal errors\n              * trace - record very detailed logging + debug + info + warn + error + fatal errors\n              * off - turn off all logging (doesn't affect metrics or audit)\n              */\n             level: \"info\",\n             /** Whether or not to include metric events in the log output */\n             metrics: false,\n             /** Whether or not to include audit events in the log output */\n             audit: false\n         }\n     },\n\n     /** Context Storage\n      * The following property can be used to enable context storage. The configuration\n      * provided here will enable file-based context that flushes to disk every 30 seconds.\n      * Refer to the documentation for further options: https://nodered.org/docs/api/context/\n      */\n     //contextStorage: {\n     //    default: {\n     //        module:\"localfilesystem\"\n     //    },\n     //},\n\n     /** `global.keys()` returns a list of all properties set in global context.\n      * This allows them to be displayed in the Context Sidebar within the editor.\n      * In some circumstances it is not desirable to expose them to the editor. The\n      * following property can be used to hide any property set in `functionGlobalContext`\n      * from being list by `global.keys()`.\n      * By default, the property is set to false to avoid accidental exposure of\n      * their values. Setting this to true will cause the keys to be listed.\n      */\n     exportGlobalContextKeys: false,\n\n     /** Configure how the runtime will handle external npm modules.\n      * This covers:\n      *  - whether the editor will allow new node modules to be installed\n      *  - whether nodes, such as the Function node are allowed to have their\n      * own dynamically configured dependencies.\n      * The allow/denyList options can be used to limit what modules the runtime\n      * will install/load. It can use '*' as a wildcard that matches anything.\n      */\n     externalModules: {\n         // autoInstall: false,   /** Whether the runtime will attempt to automatically install missing modules */\n         // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */\n         // palette: {              /** Configuration for the Palette Manager */\n         //     allowInstall: true, /** Enable the Palette Manager in the editor */\n         //     allowUpload: true,  /** Allow module tgz files to be uploaded and installed */\n         //     allowList: [],\n         //     denyList: []\n         // },\n         // modules: {              /** Configuration for node-specified modules */\n         //     allowInstall: true,\n         //     allowList: [],\n         //     denyList: []\n         // }\n     },\n\n\n/*******************************************************************************\n * Editor Settings\n *  - disableEditor\n *  - editorTheme\n ******************************************************************************/\n\n    /** The following property can be used to disable the editor. The admin API\n     * is not affected by this option. To disable both the editor and the admin\n     * API, use either the httpRoot or httpAdminRoot properties\n     */\n    //disableEditor: false,\n\n    /** Customising the editor\n     * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes\n     * for all available options.\n     */\n    editorTheme: {\n        /** The following property can be used to set a custom theme for the editor.\n         * See https://github.com/node-red-contrib-themes/theme-collection for\n         * a collection of themes to chose from.\n         */\n        //theme: \"\",\n        palette: {\n            /** The following property can be used to order the categories in the editor\n             * palette. If a node's category is not in the list, the category will get\n             * added to the end of the palette.\n             * If not set, the following default order is used:\n             */\n            //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],\n        },\n        projects: {\n            /** To enable the Projects feature, set this value to true */\n            enabled: false,\n            workflow: {\n                /** Set the default projects workflow mode.\n                 *  - manual - you must manually commit changes\n                 *  - auto - changes are automatically committed\n                 * This can be overridden per-user from the 'Git config'\n                 * section of 'User Settings' within the editor\n                 */\n                mode: \"manual\"\n            }\n        },\n        codeEditor: {\n            /** Select the text editor component used by the editor.\n             * As of Node-RED V3, this defaults to \"monaco\", but can be set to \"ace\" if desired\n             */\n            lib: \"monaco\",\n            options: {\n                /** The follow options only apply if the editor is set to \"monaco\"\n                 *\n                 * theme - must match the file name of a theme in\n                 * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme\n                 * e.g. \"tomorrow-night\", \"upstream-sunburst\", \"github\", \"my-theme\"\n                 */\n                theme: \"vs\",\n                /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.\n                 * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html\n                 */\n                //fontSize: 14,\n                //fontFamily: \"Cascadia Code, Fira Code, Consolas, 'Courier New', monospace\",\n                //fontLigatures: true,\n            }\n        }\n    },\n\n/*******************************************************************************\n * Node Settings\n *  - fileWorkingDirectory\n *  - functionGlobalContext\n *  - functionExternalModules\n *  - nodeMessageBufferMaxLength\n *  - ui (for use with Node-RED Dashboard)\n *  - debugUseColors\n *  - debugMaxLength\n *  - execMaxBufferSize\n *  - httpRequestTimeout\n *  - mqttReconnectTime\n *  - serialReconnectTime\n *  - socketReconnectTime\n *  - socketTimeout\n *  - tcpMsgQueueSize\n *  - inboundWebSocketTimeout\n *  - tlsConfigDisableLocalFiles\n *  - webSocketNodeVerifyClient\n ******************************************************************************/\n\n    /** The working directory to handle relative file paths from within the File nodes\n     * defaults to the working directory of the Node-RED process.\n     */\n    //fileWorkingDirectory: \"\",\n\n    /** Allow the Function node to load additional npm modules directly */\n    functionExternalModules: true,\n\n    /** The following property can be used to set predefined values in Global Context.\n     * This allows extra node modules to be made available with in Function node.\n     * For example, the following:\n     *    functionGlobalContext: { os:require('os') }\n     * will allow the `os` module to be accessed in a Function node using:\n     *    global.get(\"os\")\n     */\n//    functionGlobalContext: {\n        // os:require('os'),\n  //  },\nfunctionGlobalContext: { // enables and pre-populates the context.global variable\n    os:require('os'),\n    path:require('path'),\n    fs:require('fs')\n    },\n    /** The maximum number of messages nodes will buffer internally as part of their\n     * operation. This applies across a range of nodes that operate on message sequences.\n     * defaults to no limit. A value of 0 also means no limit is applied.\n     */\n    //nodeMessageBufferMaxLength: 0,\n\n    /** If you installed the optional node-red-dashboard you can set it's path\n     * relative to httpNodeRoot\n     * Other optional properties include\n     *  readOnly:{boolean},\n     *  middleware:{function or array}, (req,res,next) - http middleware\n     *  ioMiddleware:{function or array}, (socket,next) - socket.io middleware\n     */\n    ui: { path: \"\" },\n\n    /** Colourise the console output of the debug node */\n    //debugUseColors: true,\n\n    /** The maximum length, in characters, of any message sent to the debug sidebar tab */\n    debugMaxLength: 1000,\n\n    /** Maximum buffer size for the exec node. Defaults to 10Mb */\n    //execMaxBufferSize: 10000000,\n\n    /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */\n    //httpRequestTimeout: 120000,\n\n    /** Retry time in milliseconds for MQTT connections */\n    mqttReconnectTime: 15000,\n\n    /** Retry time in milliseconds for Serial port connections */\n    serialReconnectTime: 15000,\n\n    /** Retry time in milliseconds for TCP socket connections */\n    //socketReconnectTime: 10000,\n\n    /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */\n    //socketTimeout: 120000,\n\n    /** Maximum number of messages to wait in queue while attempting to connect to TCP socket\n     * defaults to 1000\n     */\n    //tcpMsgQueueSize: 2000,\n\n    /** Timeout in milliseconds for inbound WebSocket connections that do not\n     * match any configured node. Defaults to 5000\n     */\n    //inboundWebSocketTimeout: 5000,\n\n    /** To disable the option for using local files for storing keys and\n     * certificates in the TLS configuration node, set this to true.\n     */\n    //tlsConfigDisableLocalFiles: true,\n\n    /** The following property can be used to verify websocket connection attempts.\n     * This allows, for example, the HTTP request headers to be checked to ensure\n     * they include valid authentication information.\n     */\n    //webSocketNodeVerifyClient: function(info) {\n    //    /** 'info' has three properties:\n    //    *   - origin : the value in the Origin header\n    //    *   - req : the HTTP request\n    //    *   - secure : true if req.connection.authorized or req.connection.encrypted is set\n    //    *\n    //    * The function should return true if the connection should be accepted, false otherwise.\n    //    *\n    //    * Alternatively, if this function is defined to accept a second argument, callback,\n    //    * it can be used to verify the client asynchronously.\n    //    * The callback takes three arguments:\n    //    *   - result : boolean, whether to accept the connection or not\n    //    *   - code : if result is false, the HTTP error status to return\n    //    *   - reason: if result is false, the HTTP reason string to return\n    //    */\n    //},\n}\n"
  },
  {
    "path": "update/main/Arducam.py",
    "content": "import time\nimport os\n\ntry:\n    import v4l2\nexcept Exception as e:\n    print(e)\n    print(\"Try to install v4l2-fix\")\n    try:\n        from pip import main as pipmain\n    except ImportError:\n        from pip._internal import main as pipmain\n    pipmain(['install', 'v4l2-fix'])\n    print(\"\\nTry to run the focus program again.\")\n    exit(0)\n\nimport fcntl\nimport errno\n\n# # Type\n# v4l2.V4L2_CTRL_TYPE_INTEGER\n# v4l2.V4L2_CTRL_TYPE_BOOLEAN\n# v4l2.V4L2_CTRL_TYPE_MENU\n# v4l2.V4L2_CTRL_TYPE_BUTTON\n# v4l2.V4L2_CTRL_TYPE_INTEGER64\n# v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n# # Flags\n# v4l2.V4L2_CTRL_FLAG_DISABLED\n# v4l2.V4L2_CTRL_FLAG_GRABBED\n# v4l2.V4L2_CTRL_FLAG_READ_ONLY\n# v4l2.V4L2_CTRL_FLAG_UPDATE\n# v4l2.V4L2_CTRL_FLAG_INACTIVE\n# v4l2.V4L2_CTRL_FLAG_SLIDER\n\ndef assert_valid_queryctrl(queryctrl):\n    return queryctrl.type & (\n        v4l2.V4L2_CTRL_TYPE_INTEGER\n        | v4l2.V4L2_CTRL_TYPE_BOOLEAN\n        | v4l2.V4L2_CTRL_TYPE_MENU\n        | v4l2.V4L2_CTRL_TYPE_BUTTON\n        | v4l2.V4L2_CTRL_TYPE_INTEGER64\n        | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n        | 7\n        | 8\n        | 9\n    ) and queryctrl.flags & (\n        v4l2.V4L2_CTRL_FLAG_DISABLED\n        | v4l2.V4L2_CTRL_FLAG_GRABBED\n        | v4l2.V4L2_CTRL_FLAG_READ_ONLY\n        | v4l2.V4L2_CTRL_FLAG_UPDATE\n        | v4l2.V4L2_CTRL_FLAG_INACTIVE\n        | v4l2.V4L2_CTRL_FLAG_SLIDER\n    )\n\ndef get_device_controls_menu(fd, queryctrl):\n    querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum)\n    while querymenu.index <= queryctrl.maximum:\n        fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu)\n        yield querymenu\n        querymenu.index += 1\n\ndef get_device_controls_by_class(fd, control_class):\n    # enumeration by control class\n    queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n    while True:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n        except IOError as e:\n            assert e.errno == errno.EINVAL\n            break\n        if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class:\n            break\n        yield queryctrl\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n\ndef getdict(struct):\n    val = dict((field, getattr(struct, field)) for field, _ in struct._fields_)\n    val.pop(\"reserved\")\n    return val\n\ndef get_device_controls(fd):\n    # original enumeration method\n    queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE)\n    while queryctrl.id < v4l2.V4L2_CID_LASTP1:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n            print(queryctrl.name)\n        except IOError as e:\n            # this predefined control is not supported by this device\n            assert e.errno == errno.EINVAL\n            queryctrl.id += 1\n            continue\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1)\n\ndef get_ctrls(vd):\n    ctrls = []\n    # enumeration by control class\n    for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA):\n        for queryctrl in get_device_controls_by_class(vd, class_):\n            ctrl = getdict(queryctrl)\n            if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    # print(querymenu.name)\n                    ctrl[\"menu\"].append(querymenu.name)\n\n            if queryctrl.type == 9:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    ctrl[\"menu\"].append(querymenu.index)\n            ctrls.append(ctrl)\n    return ctrls\n\ndef set_ctrl(vd, id, value):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    ctrl.value = value\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n\ndef get_ctrl(vd, id):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n        return None\n    return ctrl.value\n\n\nclass Focuser:\n    FOCUS_ID = 0x009a090a\n    dev = None\n\n    def __init__(self, dev=0):\n        self.focus_value = 0\n        self.dev = dev\n\n        if type(dev) == int or (type(dev) == str and dev.isnumeric()):\n            self.dev = \"/dev/video{}\".format(dev)\n\n        self.fd = open(self.dev, 'r')\n        self.ctrls = get_ctrls(self.fd)\n        self.hasFocus = False\n        for ctrl in self.ctrls:\n            if ctrl['id'] == Focuser.FOCUS_ID:\n                self.hasFocus = True\n                self.opts[Focuser.OPT_FOCUS][\"MIN_VALUE\"] = ctrl['minimum']\n                self.opts[Focuser.OPT_FOCUS][\"MAX_VALUE\"] = ctrl['maximum']\n                self.opts[Focuser.OPT_FOCUS][\"DEF_VALUE\"] = ctrl['default']\n                self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID)\n\n        if not self.hasFocus:\n            raise RuntimeError(\"Device {} has no focus_absolute control.\".format(self.dev))\n\n    def read(self):\n        return self.focus_value\n\n    def write(self, value):\n        self.focus_value = value\n        # os.system(\"v4l2-ctl -d {} -c focus_absolute={}\".format(self.dev, value))\n        set_ctrl(self.fd, Focuser.FOCUS_ID, value)\n\n    OPT_BASE    = 0x1000\n    OPT_FOCUS   = OPT_BASE | 0x01\n    OPT_ZOOM    = OPT_BASE | 0x02\n    OPT_MOTOR_X = OPT_BASE | 0x03\n    OPT_MOTOR_Y = OPT_BASE | 0x04\n    OPT_IRCUT   = OPT_BASE | 0x05\n    opts = {\n        OPT_FOCUS : {\n            \"MIN_VALUE\": 0,\n            \"MAX_VALUE\": 1000,\n            \"DEF_VALUE\": 0,\n        },\n    }\n    def reset(self,opt,flag = 1):\n        info = self.opts[opt]\n        if info == None or info[\"DEF_VALUE\"] == None:\n            return\n        self.set(opt,info[\"DEF_VALUE\"])\n\n    def get(self,opt,flag = 0):\n        info = self.opts[opt]\n        return self.read()\n\n    def set(self,opt,value,flag = 1):\n        info = self.opts[opt]\n        if value > info[\"MAX_VALUE\"]:\n            value = info[\"MAX_VALUE\"]\n        elif value < info[\"MIN_VALUE\"]:\n            value = info[\"MIN_VALUE\"]\n        self.write(value)\n        print(\"write: {}\".format(value))\n\n    def __del__(self):\n        self.fd.close()\n\npass\n"
  },
  {
    "path": "update/main/OpenScan.py",
    "content": "basepath = '/home/pi/OpenScan/'\nfrom os.path import isfile\n\ndef load_bool(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    if value == '1' or value == 'True' or value =='true':\n        value = True\n    else:\n        value = False\n    return value\n\ndef load_str(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    return value\n\ndef load_int(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = int(file.read().replace('\\n',''))\n    return value\n\ndef load_float(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = float(file.read().replace('\\n',''))\n    return value\n\ndef save(name, value):\n    filename = basepath+'settings/'+name\n    with open(filename, 'w+') as file:\n        file.write(str(value))\n    return\n\ndef OpenScanCloud(cmd, msg):\n    from requests import get\n    osc_user = 'openscan'\n    osc_pw = 'free'\n    osc_server = 'http://openscanfeedback.dnsuser.de:1334/'\n\n    try:\n        r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg)\n    except:\n        r = type('obj', (object,), {'status_code' : 404, 'text':None})\n    return r\n\ndef camera(cmd, msg = {}):\n    from requests import get\n    flask = 'http://127.0.0.1:1312/'\n    try:\n        r = get(flask + cmd, params=msg)\n        return r.status_code\n    except:\n        return 400\n\ndef motorrun(motor,angle):\n    import RPi.GPIO as GPIO\n    from time import sleep\n    from math import cos\n    msg = {'cmd':'set'}\n    camera('/ping', msg)\n\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n\n    spr = load_int(motor + '_stepsperrotation')\n    dirpin = load_int('pin_' + motor + '_dir')\n    steppin = load_int('pin_' + motor +'_step')\n    dir = load_int(motor + '_dir')\n    ramp = load_int(motor + '_accramp')\n    acc = load_float(motor + '_acc')\n    delay_init = load_float(motor + '_delay')\n    delay = delay_init\n\n    step_count=int(angle*spr/360) * dir\n    GPIO.setup(dirpin, GPIO.OUT)\n    GPIO.setup(steppin, GPIO.OUT)\n    if (step_count>0):\n        GPIO.output(dirpin, GPIO.HIGH)\n    if(step_count<0):\n        GPIO.output(dirpin, GPIO.LOW)\n        step_count=-step_count\n    for x in range(step_count):\n        GPIO.output(steppin, GPIO.HIGH)\n        if x<=ramp and x<=step_count/2:\n            delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc)\n            #delay=delay_init+(ramp-x)*(delay_init)/acc\n        elif step_count-x<=ramp and x>step_count/2:\n            delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc)\n            #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc\n        else:\n            delay = delay_init\n        sleep(delay)\n        GPIO.output(steppin, GPIO.LOW)\n        sleep(delay)\n\ndef ringlight(number,state):\n    import RPi.GPIO as GPIO\n    msg = {'cmd':'set'}\n    camera('/ping', msg)\n    pin = load_int('pin_ringlight' + str(number))\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, state)\n\ndef take_photo(file):\n    from os import system\n    filepath = basepath + file\n\n    model=load_str('model')\n\n\n\n    shutter = str(load_int('cam_shutter'))\n    saturation = load_str('cam_saturation')\n    contrast = load_str('cam_contrast')\n    awbg_red = load_str('cam_awbg_red')\n    awbg_blue = load_str('cam_awbg_blue')\n    gain = load_str('cam_gain')\n    quality = load_int('cam_jpeg_quality')\n    filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg'\n    #width = load_str('cam_resx')\n    #height = load_str('cam_resy')\n    timeout = load_str('cam_timeout')\n    cropx = load_int('cam_cropx')/200\n    cropy = load_int('cam_cropy')/200\n    rotation = load_int('cam_rotation')\n    AF = load_bool('cam_AFmode')\n    camera = load_str('camera')\n\n\n    if camera == 'imx519' and AF == True:\n        autofocus = ' --autofocus '\n    else:\n        autofocus = ''\n\n    if camera  == \"usb_webcam\":\n        cmd = 'fswebcam -i 0 -r \"1280x720\" -F 5 --no-banner --jpeg 95 --save ' + filepath2\n    else:\n        cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1'\n    #    cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus\n        \n    system(cmd)\n    return cmd\n\ndef get_points(samples=1):\n    from math import pi, sqrt, acos, atan2, cos, sin\n\n    points = []\n    phi = pi * (3. - sqrt(5.))\n    for i in range(int(samples)):\n        y = 1 - (i / float(samples - 1)) * 2\n        radius = sqrt(1 - y * y)\n        theta = phi * i\n        x = cos(theta) * radius\n        z = sin(theta) * radius\n        r=sqrt(x*x+y*y+z*z)\n        theta_neu=acos(z/r)*180/pi\n        phi_neu=atan2(y,x)*180/pi\n        points.append((theta_neu-90,phi_neu))\n    points.sort()\n    return points\n\ndef create_coordinates(angle_min, angle_max,point_count):\n    point_count_final=point_count\n    if angle_max < angle_min:\n        a = angle_min\n        angle_min = angle_max\n        angle_max = a\n    point_count=point_count*90/(angle_max-angle_min)\n    actual_points=0\n    while actual_points<point_count_final:\n        points=get_points(point_count)\n        filtered=[]\n        for x,y in points:\n            if x>angle_min and x<angle_max and len(filtered)<point_count_final:\n                filtered.append((x,y))\n        actual_points=len(filtered)\n\n        if point_count-actual_points>20:\n            point_count=point_count+3\n        else:\n            point_count=point_count+1\n    return filtered\n\n"
  },
  {
    "path": "update/main/config.txt",
    "content": "# For more options and information see\n# http://rpf.io/configtxt\n# Some settings may impact device functionality. See link above for details\n\n\n# uncomment if you get no picture on HDMI for a default \"safe\" mode\n#hdmi_safe=1\nhdmi_blanking=2\n\n# uncomment the following to adjust overscan. Use positive numbers if console\n# goes off screen, and negative if there is too much border\n#overscan_left=16\n#overscan_right=16\n#overscan_top=16\n#overscan_bottom=16\n\n# uncomment to force a console size. By default it will be display's size minus\n# overscan.\n#framebuffer_width=1280\n#framebuffer_height=720\n\n# uncomment if hdmi display is not detected and composite is being output\n#hdmi_force_hotplug=1\n\n# uncomment to force a specific HDMI mode (this will force VGA)\n#hdmi_group=1\n#hdmi_mode=1\n\n# uncomment to force a HDMI mode rather than DVI. This can make audio work in\n# DMT (computer monitor) modes\n#hdmi_drive=2\n\n# uncomment to increase signal to HDMI, if you have interference, blanking, or\n# no display\n#config_hdmi_boost=4\n\n# uncomment for composite PAL\n#sdtv_mode=2\n\n#uncomment to overclock the arm. 700 MHz is the default.\n#arm_freq=800\n\n# Uncomment some or all of these to enable the optional hardware interfaces\n#dtparam=i2c_arm=on\n#dtparam=i2s=on\n#dtparam=spi=on\n\n# Uncomment this to enable infrared communication.\n#dtoverlay=gpio-ir,gpio_pin=17\n#dtoverlay=gpio-ir-tx,gpio_pin=18\n\n# Additional overlays and parameters are documented /boot/overlays/README\n\n# Enable audio (loads snd_bcm2835)\ndtparam=audio=on\n\n# Automatically load overlays for detected cameras\ncamera_auto_detect=0\n\n# Automatically load overlays for detected DSI displays\ndisplay_auto_detect=1\n\n# Enable DRM VC4 V3D driver\n#dtoverlay=vc4-kms-v3d\nmax_framebuffers=2\n\n# Disable compensation for displays with overscan\ndisable_overscan=1\n\n[cm4]\n# Enable host mode on the 2711 built-in XHCI USB controller.\n# This line should be removed if the legacy DWC2 controller is required\n# (e.g. for USB device mode) or if USB support is not required.\notg_mode=1\n\n[pi4]\n# Run as fast as firmware / board allows\narm_boost=1\n\n[all]\n\ncamera_auto_detect=0\ngpu_mem=256\ndtoverlay=vc4-fkms-v3d\ndtoverlay=imx519,media-controller=1\n"
  },
  {
    "path": "update/main/fla.py",
    "content": "from flask import Flask, make_response, jsonify, request, abort\nfrom PIL import Image\nimport gphoto2 as gp\nfrom time import sleep, time\nimport shutil\nfrom OpenScan import load_int, load_str, load_float, load_bool, ringlight\nimport RPi.GPIO as GPIO\nfrom math import sqrt\nimport os \n\nGPIO.setwarnings(False)\nGPIO.setmode(GPIO.BCM)\n\napp = Flask(__name__)\n\nbasedir = '/home/pi/OpenScan/'\ntimer = time()\n\n\n###################################################################################################################\n@app.route('/shutdown', methods=['get'])\ndef shutdown():\n    delay = 0.2\n    ringlight(2, False)\n    for i in range(5):\n        ringlight(1, True)\n        sleep(delay)\n        ringlight(1, False)\n        sleep(delay)\n    os.system('shutdown -h now')\n    return \"Shutting down...\", 200\n###################################################################################################################\n@app.route('/reboot', methods=['get'])\ndef reboot():\n    delay = 0.2\n    ringlight(2, False)\n\n    for i in range(5):\n        ringlight(1, True)\n        sleep(delay)\n        ringlight(1, False)\n        sleep(delay)\n    os.system('reboot -h')\n    return \"Rebooting...\", 200\n###################################################################################################################\n@app.route('/ping', methods=['get'])\ndef ping():\n    global timer\n    cmd = str(request.args.get('cmd'))\n    if cmd == 'set':\n        timer = time()\n    inactive = time() - timer\n    return ({'inactive':inactive}, 200)\n###################################################################################################################\n@app.route('/gphoto_init', methods=['get'])\ndef gphoto_init():\n    global camera\n    camera = gp.Camera()\n    camera.init()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_preview', methods=['get'])\ndef gphoto_preview():\n    filepath = str(request.args.get('filepath'))\n    camera_file = gp.gp_camera_capture_preview(camera)[1]\n    target = basedir + filepath\n    camera_file.save(target)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_capture', methods=['get'])\ndef gphoto_capture():\n    filepath = str(request.args.get('filepath'))\n    file_path = camera.capture(gp.GP_CAPTURE_IMAGE)\n    camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL)\n    camera_file.save(basedir + filepath)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_test', methods=['get'])\ndef gphoto_test():\n    text = camera.get_summary()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_exit', methods=['get'])\ndef gphoto_exit():\n    global camera\n    camera.exit()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/crop', methods=['get'])\ndef crop():\n    output_downscale = load_bool('cam_output_downscale')\n    output_resolution = load_int('cam_output_resolution')\n    preview_resolution = load_int('cam_preview_resolution')\n    filepath_in = basedir + str(request.args.get('filepath_in'))\n    filepath_out = basedir + str(request.args.get('filepath_out'))\n    cropx = int(request.args.get('cropx'))/200\n    cropy = int(request.args.get('cropy'))/200\n    rotation = int(request.args.get('rotation'))\n    preview = str(request.args.get('preview'))\n    downscale = 1\n\n    with Image.open(filepath_in) as img:\n        w,h = img.size\n        if cropx != 0 or cropy != 0:\n            img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy)))\n        if rotation == 90:\n            img  = img.transpose(Image.ROTATE_90)\n        elif rotation == 180:\n            img= img.transpose(Image.ROTATE_180)\n        elif rotation == 270:\n            img= img.transpose(Image.ROTATE_270)\n\n        if preview == \"True\":\n            w,h = img.size\n            factor = (w*h)/preview_resolution\n            if factor > 1:\n                img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS)\n\n        elif output_downscale == True:\n            w,h = img.size\n            factor = (w*h)/output_resolution\n            if factor > 1:\n                img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) \n\n        img.save(filepath_out, quality=95, subsampling=0)\n\n    return ({}, 200)\n\n###################################################################################################################\n@app.route('/external_capture', methods=['get'])\ndef external_capture():\n    pin = load_int('pin_external')\n    delay_before = load_float('cam_delay_before')\n    timeout = load_float('cam_timeout')/1000\n    delay_after = load_float('cam_delay_after')\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_before)\n    GPIO.output(pin, GPIO.HIGH)\n    sleep(timeout)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_after)\n    return ({}, 200)\n\n\n\n\nif __name__ == '__main__':\n#    app.run(host='127.0.0.1', port=1312, debug=False, threaded=True)\n    app.run(host='0.0.0.0', port=1312, debug=False, threaded=True)\n"
  },
  {
    "path": "update/main/flows.json",
    "content": "[\n    {\n        \"id\": \"829d803b6033a693\",\n        \"type\": \"tab\",\n        \"label\": \"HOME\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"1613373abaf77a2c\",\n        \"type\": \"tab\",\n        \"label\": \"SCAN\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"4981d84ef1a366d1\",\n        \"type\": \"tab\",\n        \"label\": \"Files&Cloud\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"017bd4e4a428bee5\",\n        \"type\": \"tab\",\n        \"label\": \"SETTINGS\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"c8e7ecb5849edb9a\",\n        \"type\": \"tab\",\n        \"label\": \"UPDATE\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"b3150b13e34b1fe8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"OpenScan\",\n        \"icon\": \"dashboard\",\n        \"order\": 1,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"b6e9c2df6b28ff66\",\n        \"type\": \"ui_base\",\n        \"theme\": {\n            \"name\": \"theme-dark\",\n            \"lightTheme\": {\n                \"default\": \"#0094CE\",\n                \"baseColor\": \"#0094CE\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"darkTheme\": {\n                \"default\": \"#097479\",\n                \"baseColor\": \"#097479\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"customTheme\": {\n                \"name\": \"Untitled Theme 1\",\n                \"default\": \"#4B7930\",\n                \"baseColor\": \"#4B7930\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n            },\n            \"themeState\": {\n                \"base-color\": {\n                    \"default\": \"#097479\",\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-titlebar-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-backgroundColor\": {\n                    \"value\": \"#111111\",\n                    \"edited\": false\n                },\n                \"page-sidebar-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"group-textColor\": {\n                    \"value\": \"#0eb8c0\",\n                    \"edited\": false\n                },\n                \"group-borderColor\": {\n                    \"value\": \"#555555\",\n                    \"edited\": false\n                },\n                \"group-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"widget-textColor\": {\n                    \"value\": \"#eeeeee\",\n                    \"edited\": false\n                },\n                \"widget-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"widget-borderColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"base-font\": {\n                    \"value\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n                }\n            },\n            \"angularTheme\": {\n                \"primary\": \"indigo\",\n                \"accents\": \"blue\",\n                \"warn\": \"red\",\n                \"background\": \"grey\",\n                \"palette\": \"light\"\n            }\n        },\n        \"site\": {\n            \"name\": \"OpenScan 3D Scanner\",\n            \"hideToolbar\": \"false\",\n            \"allowSwipe\": \"false\",\n            \"lockMenu\": \"false\",\n            \"allowTempTheme\": \"true\",\n            \"dateFormat\": \"DD/MM/YYYY\",\n            \"sizes\": {\n                \"sx\": 46,\n                \"sy\": 46,\n                \"gx\": 10,\n                \"gy\": 10,\n                \"cx\": 6,\n                \"cy\": 6,\n                \"px\": 6,\n                \"py\": 6\n            }\n        }\n    },\n    {\n        \"id\": \"729f9ea6e3513c9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Home\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"65ae49b64fa0d83e\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Settings\",\n        \"icon\": \"dashboard\",\n        \"order\": 4,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"4fe6b4c0ade0938a\",\n        \"type\": \"ui_group\",\n        \"name\": \"General\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"0fe66c9190b8a87c\",\n        \"type\": \"ui_group\",\n        \"name\": \"Network\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"93aadb71dee6d977\",\n        \"type\": \"ui_group\",\n        \"name\": \"Camera\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 4,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d49a6dfd7fb17096\",\n        \"type\": \"ui_group\",\n        \"name\": \"Motor\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 5,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"644b3bcc903d46ca\",\n        \"type\": \"ui_group\",\n        \"name\": \"Pinout\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 6,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": true,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"e23b837a9f040895\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Scan\",\n        \"icon\": \"dashboard\",\n        \"order\": 2,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"7aaf184330605300\",\n        \"type\": \"ui_group\",\n        \"name\": \"Settings\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"ce9cc9d915dc6eb6\",\n        \"type\": \"ui_group\",\n        \"name\": \"Picamera\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"12\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"90223f7ddc082321\",\n        \"type\": \"ui_group\",\n        \"name\": \"Arducam\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": 12,\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"7625f9c9e8dbc5c6\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3b4bd36726be16d5\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 3,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"b5fdd57b.15eda8\",\n        \"type\": \"ui_group\",\n        \"name\": \"Main\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": 13,\n        \"collapse\": false\n    },\n    {\n        \"id\": \"db43d646.2074c8\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"15a222ed.d70a7d\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Files&Cloud\",\n        \"icon\": \"dashboard\",\n        \"order\": 3,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"ddbd496e.93a288\",\n        \"type\": \"ui_group\",\n        \"name\": \"Manage Updates\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"3ce32450.e0cffc\",\n        \"type\": \"ui_group\",\n        \"name\": \"System & Stats\",\n        \"tab\": \"d25e08b4.5b27e8\",\n        \"order\": 2,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"d25e08b4.5b27e8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Update & Info\",\n        \"icon\": \"dashboard\",\n        \"order\": 5,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"1f7f7e1e24f5ad9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Initialize\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"5b3e5aca21140e9a\",\n        \"type\": \"ui_group\",\n        \"name\": \"Update\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"3b4961c4e72ff58a\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"5ef40dca2c6c6aab\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 11,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"bdd26746cc1e1ba0\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3584b5ef2b7acb72\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"cac67f0e.f01fa\",\n        \"type\": \"ui_group\",\n        \"name\": \"Button Top\",\n        \"tab\": \"\",\n        \"order\": 1,\n        \"disp\": true,\n        \"width\": \"6\",\n        \"collapse\": false\n    },\n    {\n        \"id\": \"c178a6075244d901\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 6,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"f8d7cbc04f8cbcba\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 8,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"bed9a462261844dc\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 14,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"42acb237087cf7f1\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 15,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"dc9e2987583f2cbb\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 16,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3fe52603e2ac73b6\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"name\": \"Background\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"format\": \"<style>\\n    body {\\n        background-image: url(\\\"/files/logo.jpg\\\");\\n        background-repeat: no-repeat;\\n        background-attachment: fixed;\\n        background-position: center;\\n\\n    }\\n    #OpenScan_Home {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n    [node-id=\\\"a291fc98e4269c1b\\\"] {\\n    background-color: transparent !important;\\n    }\\n\\n    \\n    \\n    #OpenScan_Update {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n</style>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 110,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4468f691.103eb8\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"SCAN\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"1\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6560dd25.9e76c4\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Settings\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"3\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd5288.2805fc\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"events\": \"all\",\n        \"x\": 280,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"71e72293.91c6fc\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Files\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"2\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e7306ef2.3b4df\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Update&Info\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"4\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"88edad7ca53698fd\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"x\": 90,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"000a811a215e08d4\",\n                \"83c2b5ea51f0fec3\",\n                \"88fde4ab78c965d7\",\n                \"bee62d2a99cbc63b\",\n                \"8e39e4a037487ecd\",\n                \"bb84b9e5c7d8e21f\",\n                \"7113d7b25a851151\",\n                \"c4c1580c289fc7bd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bd75f33b8a57c522\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"92c98e6ce7cd25f9\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 335,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"000a811a215e08d4\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"func\": \"msg.enabled = true\\nmsg.payload = 1\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"bd75f33b8a57c522\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"83c2b5ea51f0fec3\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 240,\n        \"y\": 480,\n        \"wires\": [\n            [\n                \"6b94bf2295b1b31d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6b94bf2295b1b31d\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\"\n        ],\n        \"x\": 335,\n        \"y\": 480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"88fde4ab78c965d7\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_cloud'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = 'ready'\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\\n  \\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"960912e90ba5b5bc\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\"\n        ],\n        \"x\": 615,\n        \"y\": 800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"168d72a54504b327\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"5/0.1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"5\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"x\": 100,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"6c6ef2255a7d39e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6c6ef2255a7d39e5\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"repeat 5s/0.1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"61990987acd0f263\",\n            \"2415272f42ce468c\"\n        ],\n        \"x\": 195,\n        \"y\": 720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bee62d2a99cbc63b\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"global\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nglobal.set('combine', false)\\nglobal.set('focus', 2838)\\nglobal.set('focus1', 0)\\nglobal.set('focus2', 0)\\n\\nglobal.set('focuser', true)\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"f20da2fc4978b7bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"544d20f02215011a\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"CREATE FACTORY DEFAULT\",\n        \"func\": \"msg = { \\n'overwrite':msg.overwrite,\\n'settings':\\n    {\\n    'advanced_settings':false,\\n    'cameras':{\\n        'imx519':[4656,3496],\\n        'imx219':[3280,2464],\\n        'imx477':[4056,3040],\\n        'ov5647':[2592,1944],\\n        'imx378':[3840,2880],\\n        'ov9271':[1280,800],\\n        'imx290a':[1920,1080],\\n        'imx290b':[1920,1080],\\n    },\\n    'cam_AFmode':true,\\n    'cam_STmode':true,\\n    'cam_stacksize':2,\\n    'cam_awbg_blue':0,\\n    'cam_awbg_red':0,\\n    'cam_contrast':1,\\n    'cam_cropx':0,\\n    'cam_cropy':0,\\n    'cam_delay_after':0,\\n    'cam_delay_before':0,\\n    'camera':'',\\n    'cam_gain':1,\\n    'cam_jpeg_quality':95,\\n    'cam_rotation':0,\\n    'cam_saturation':1,\\n    'cam_shutter':50000,\\n    'cam_timeout':800,\\n    'hostname':'openscan',\\n    'model':'',\\n    'osc_credit':'',\\n    'osc_limit_filesize':'',\\n    'osc_limit_photos':'',\\n    'osc_splitsize':200000000,\\n    'pin_endstop1':24,\\n    'pin_endstop2':25,\\n    'pin_external':10,\\n    'pin_ringlight1':17,\\n    'pin_ringlight2':27,\\n    'pin_rotor_dir':5,\\n    'pin_rotor_enable':23,\\n    'pin_rotor_step':6,\\n    'pin_tt_dir':9,\\n    'pin_tt_enable':22,\\n    'pin_tt_step':11,\\n    'rotor_acc':1,\\n    'rotor_accramp':2000,\\n    'rotor_angle':10,\\n    'rotor_anglemax':60,\\n    'rotor_anglemin':-20,\\n    'rotor_anglestart':0,\\n    'rotor_delay':0.0001,\\n    'rotor_dir':1,\\n    'rotor_stepsperrotation':48000,\\n    'routine_photocount':50,\\n    'routine_projectname':'default',\\n    'smb':true,\\n    'ssh':true,\\n    'status_cloud':'ready',\\n    'status_internal_cam':'--READY--',\\n    'status_uploadprogress':'',\\n    'terms':false,\\n    'token':'',\\n    'tt_acc':1,\\n    'tt_accramp':200,\\n    'tt_angle':10,\\n    'tt_delay':0.0001,\\n    'tt_dir':1,\\n    'tt_stepsperrotation':3200,\\n    'cam_focus':2838,\\n    'cam_focus1':0,\\n    'cam_focus2':0,\\n    'uploadprogress':'',\\n    'update_type':'beta',\\n    'update_auto':true,\\n    'downscale_threshold':1000,\\n    'turntable_mode':false,\\n    'timeout_ringlight':300,\\n    'diskspace_threshold':4000,\\n    'updateable':false,\\n    'cam_focuspeak':false,\\n    'cam_histogram':false,\\n    'routine_secondpass':true,\\n    'cam_output_resolution':20000000,\\n    'cam_preview_resolution':2000000,\\n    'cam_output_downscale':false,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 310,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"c77552216a8bb781\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1f0ed7d5a9d670e\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 90,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c77552216a8bb781\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"chk files\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 520,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"960912e90ba5b5bc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38783aea9cc317a6\",\n        \"type\": \"link in\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"factory reset\",\n        \"links\": [\n            \"80bccc884b0be297\",\n            \"beacc3dc5398fa79\"\n        ],\n        \"x\": 135,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f20da2fc4978b7bf\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"global\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"d14bbbb446d45e39\"\n        ],\n        \"x\": 345,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8e39e4a037487ecd\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"create log\",\n        \"func\": \"import subprocess\\nfrom time import sleep\\nsleep(20)\\n\\n\\nlog = '############################################DMESG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"dmesg\\\")\\nlog += '\\\\n############################################SYSLOG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"tail -10000 /var/log/syslog\\\")\\n\\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\\n    file.write(log)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 240,\n        \"y\": 560,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"be8cae9cf6f3585f\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"name\": \"first start\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 3,\n        \"format\": \"<h1 style=\\\"text-align: center;\\\">Initial Setup</h1>\\n<p style=\\\"text-align: center;\\\">Note, that you can always adjust these and other settings in the settings menu, which will appear after this setup stage.&nbsp;</p>\\n<p><strong>Model</strong></p>\\n<p style=\\\"margin-left: 20px;\\\">Please select the OpenScan Version - this will only affect the motor settings (acceleration, gear ratio, speed).</p>\\n<p><strong>Camera</strong></p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>Pi Camera v1, v2, HQ, Arducam IMX519, IMX290, IMX378, OV9281</u> are connected through the ribbon cable. If you encounter any issues, please check the cable&apos;s orientation</p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>DSLR (gphoto)</u> - can be used with a wide range of cameras, which can be connected and controlled via USB. Check <a href=\\\"http://www.gphoto.org/proj/libgphoto2/support.php\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">GPhoto</a> if your camera is supported</p>\\n<p style=\\\"margin-left: 20px;\\\">- <u>External Camera</u> - Can be used to connect your camera trigger to the GPIO pins on the front of the pi shield. This can be used with any (modified) remote shutter release, and thus it is possible to use Smartphones, DSLR and compact cameras</p>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"8955d11554f55e63\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"5b3e5aca21140e9a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 3,\n        \"passthru\": false,\n        \"label\": \"Install Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 120,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"1e7457ea9c2c5e09\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1e7457ea9c2c5e09\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"update\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"39a502b38837273d\"\n        ],\n        \"x\": 245,\n        \"y\": 280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bb84b9e5c7d8e21f\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"rescue incomplete project\",\n        \"func\": \"#if project has not been done properly, this is a way to rescue the file\\n\\nfrom os import system\\nfrom os.path import isfile\\nfrom time import strftime\\nfrom OpenScan import load_str\\n\\nbasepath = '/home/pi/OpenScan/'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectname=load_str(\\\"routine_projectname\\\")\\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n\\nif isfile(zippath):\\n    system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 600,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a291fc98e4269c1b\",\n        \"type\": \"ui_text\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 7,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"version\",\n        \"label\": \"Version:\",\n        \"format\": \"{{msg.firmware}}\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 460,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7113d7b25a851151\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"FIRMWARE VERSION\",\n        \"func\": \"msg.firmware = '2024-11-11'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"a291fc98e4269c1b\",\n                \"ec5cefa70ff535f7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ec5cefa70ff535f7\",\n        \"type\": \"ui_text\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"current version\",\n        \"label\": \"Current version:\",\n        \"format\": \"{{msg.firmware}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 320,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c4c1580c289fc7bd\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"create path\",\n        \"func\": \"import os\\n\\npaths = ['/home/pi/OpenScan/scans/preview/']\\n\\n\\nfor i in paths:\\n    if not os.path.isdir(i):\\n        os.mkdir(i)\",\n        \"outputs\": 1,\n        \"x\": 250,\n        \"y\": 640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c5e5e75ed45efbaa\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"7aaf184330605300\",\n        \"name\": \"donate\",\n        \"order\": 2,\n        \"width\": \"0\",\n        \"height\": \"0\",\n        \"format\": \"<script id=\\\"titleScript\\\" type=\\\"text/javascript\\\">\\n$(function() {\\n    if ($('.md-toolbar-tools').length != 0) {\\n        load();\\n    } else {\\n        setTimeout(load, 500);\\n    }\\n});\\n\\nfunction load() {\\n    $('#bmac').remove();\\n    var toolbar = $('.md-toolbar-tools');\\n    var div = $('<div/>');\\n    var p = $('<div/>', { id: 'bmac' });\\n\\n    // Append elements\\n    div.append(p);\\n    div[0].style.margin = '0px 0px 0px auto';\\n    toolbar.append(div);\\n\\n    // Version display and buttons\\n    p.append('<small id=\\\"version\\\"></small>');\\n    p.append(' ');\\n    p.append('<a class=\\\"md-button\\\" target=\\\"_blank\\\" href=\\\"https://www.patreon.com/bePatron?u=51974655\\\"><u>DONATE</u></a>');\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:0 5 0 5!important;\\\" onclick=\\\"shutdown()\\\"><i class=\\\"fa fa-power-off\\\"></i></a>');\\n    p.append('   ');\\n    p.append('<a class=\\\"md-button2\\\" style=\\\"margin:10 0 10 0!important;\\\" onclick=\\\"reboot()\\\"><i class=\\\"fa fa-repeat\\\"></i></a>');\\n}\\n\\nfunction shutdown() {\\n    if (confirm(\\\"Are you sure you want to shut down the system?\\\")) {\\n        // Get the current hostname from the browser\\n        const baseUrl = window.location.protocol + '//' + window.location.hostname + ':1312';\\n        fetch(`${baseUrl}/shutdown`, {\\n            method: 'GET',\\n        })\\n        .then(data => {\\n            alert(\\\"Shutdown initiated. The system will power off shortly.\\\");\\n        });\\n    }\\n}\\n\\nfunction reboot() {\\n    if (confirm(\\\"Are you sure you want to reboot the system?\\\")) {\\n        // Get the current hostname from the browser\\n        const baseUrl = window.location.protocol + '//' + window.location.hostname + ':1312';\\n        fetch(`${baseUrl}/reboot`, {\\n            method: 'GET',\\n        })\\n        .then(data => {\\n            alert(\\\"Reboot initiated. The system will restart shortly.\\\");\\n        });\\n    }\\n}\\n</script>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"828e5298.d2192\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇐\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"b12e54fb.3141b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"96c7e241.458e6\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇒\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"37f52dd4.bd7572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e854876.6b6008\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇑\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"555aea34.b3b5e4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"753817f.1b9b3e8\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇓\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"9905e0c9.dddcd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8775044.3aa3ef8\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Turntable\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 100,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9e8a2d23.bf6ce\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Rotor\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"555aea34.b3b5e4\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"46e00b45.c24ca4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9905e0c9.dddcd\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"6ee089cb343a35ef\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b12e54fb.3141b8\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"c1871a2b9af5419a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"37f52dd4.bd7572\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"42b9f1fc49e69f54\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"46e00b45.c24ca4\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ee089cb343a35ef\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',-load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 370,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"42b9f1fc49e69f54\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',-load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 440,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c1871a2b9af5419a\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 350,\n        \"y\": 400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"107a030938cbfea9\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"cb6ebdabaaf7d0da\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ce28a0b5bfb0d5a1\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"84d6b96c8ebaac96\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"c8a3fde5206ce1ae\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"44c3947a9b92d32d\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"9c6b48b7b4cc4e1a\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"9daea4bd57f7a00e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c470fd0b15356206\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"87be854db758a9a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c2b2ab5524271123\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"26f17a7f406df73c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ebbf11b55d758806\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 320,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"67385b196c517ac6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4b3112a9ec6c487\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload=\\\"default\\\"\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"ebbf11b55d758806\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67385b196c517ac6\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_projectname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload).replace(/ /g, '_')\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4dd7285c2b0fd79b\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"ringlight\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"all\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": 0,\n        \"max\": \"3\",\n        \"step\": 1,\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"873dace18a23fdf2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"873dace18a23fdf2\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"LED\",\n        \"func\": \"from OpenScan import ringlight\\nval = msg['payload']\\n\\nif val == 0:\\n    ringlight(1,False)\\n    ringlight(2,False)\\nelif val == 1:\\n    ringlight(1,False)\\n    ringlight(2,True)\\nelif val == 2:\\n    ringlight(1,True)\\n    ringlight(2,False)\\nelif val == 3:\\n    ringlight(1,True)\\n    ringlight(2,True)\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 700,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"9e30e33a1520fee0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"msg.payload = 0\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"4dd7285c2b0fd79b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dd287f40385922f\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start \",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 17,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-play\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"enabled\",\n        \"topicType\": \"str\",\n        \"x\": 150,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"33d94a04b96a2de0\",\n                \"6d15f717d5a11002\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"579f2211199fd6ab\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 19,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-stop\",\n        \"payload\": \"numberofphotos\",\n        \"payloadType\": \"global\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 810,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"1787f08ed7070ddd\",\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"431f917c2541ae48\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Routine\",\n        \"func\": \"from OpenScan import load_bool, load_str, load_int, load_float, motorrun, create_coordinates, take_photo, save, load_bool, camera\\nfrom time import sleep, strftime, time\\nfrom zipfile import ZipFile, ZIP_DEFLATED\\nfrom os import system\\nfrom os.path import isfile\\nfrom Arducam import Focuser\\n\\nif load_str(\\\"status_internal_cam\\\")==\\\"no camera found\\\" or load_str(\\\"status_internal_cam\\\")[:5]==\\\"Featu\\\":\\n    return\\n\\nsave('status_internal_cam','Routine-preparing')\\n\\nprojectname=load_str(\\\"routine_projectname\\\")\\nphotocount = load_int('routine_photocount') #vorher point_count\\nangle_max = load_int('rotor_anglemax')\\nangle_min = load_int('rotor_anglemin')\\nangle_start = load_int('rotor_anglestart')\\ncam = load_str('camera')\\nSTmode = load_bool('cam_STmode')\\ntt_mode = load_bool('turntable_mode')\\ncam_delay_after = load_float('cam_delay_after')\\ncam_delay_before = load_float('cam_delay_before')\\n\\nif cam == 'imx519' and STmode == True:\\n    focuser = Focuser('/dev/v4l-subdev1')\\n    stacksize = load_int('cam_stacksize')\\n    focus1 = load_int('cam_focus1')\\n    focus2 = load_int('cam_focus2')\\n    if focus1 > focus2:\\n        focus2 = focus1\\n        focus1 = load_int('cam_focus2') \\n    focusstep = int((focus2-focus1)/(stacksize - 1))\\n\\ncounter = 0\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\n\\nif not 'projectcode' in msg:\\n    projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n    msg['projectcode'] = projectcode\\n    msg['counter'] = -1\\n    if isfile(zippath):\\n        system('rm ' + zippath)\\n    sleep(1)\\n\\nprojectcode = msg['projectcode']\\nmsg['counter'] += 1\\n\\nif tt_mode == False:\\n    coordinates = create_coordinates(angle_min,angle_max,photocount)\\nelse:\\n    angle_start = 0\\n    coordinates = []\\n    for i in range (photocount):\\n        coordinates.append([0,360/photocount*(i+1)])\\n\\nposition_last = (angle_start , 0)\\n\\nzip = ZipFile(zippath, \\\"a\\\",ZIP_DEFLATED, allowZip64=True)\\n\\nstarttime = time()\\n\\nfor position in coordinates:\\n    counter += 1\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n\\n    while load_str('status_internal_cam') == 'Routine-paused':\\n        sleep(0.2)\\n\\n    filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \\\".jpg\\\"\\n\\n    rotor_angle = position_last[0] - position[0]\\n    if abs(rotor_angle) > 180:\\n        rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\\n\\n    tt_angle = position_last[1] - position[1]\\n    if tt_angle > 180:\\n        tt_angle -= 360\\n    elif tt_angle < -180:\\n        tt_angle += 360\\n    #    tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\\n            \\n    motorrun('rotor', rotor_angle)\\n    motorrun('tt', tt_angle)\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n    while load_str('status_internal_cam') == 'Routine-paused':\\n        sleep(0.2)\\n\\n    msg['cropx'] = load_int('cam_cropx')\\n    msg['cropy'] = load_int('cam_cropy')\\n    msg['rotation'] = load_int('cam_rotation')\\n    msg['filepath_in'] = 'tmp/tmp.jpg'\\n    msg['filepath_out'] = 'tmp/tmp.jpg'\\n    msg['filepath'] = 'tmp/tmp.jpg'\\n\\n    if counter < 6:\\n        ETA = ''\\n    sleep(cam_delay_before)\\n    if STmode == True:\\n        counter2 = 0\\n        for focus in range (stacksize):\\n            if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n                break\\n            while load_str('status_internal_cam') == 'Routine-paused':\\n                sleep(0.2)\\n            counter2 += 1\\n            save('status_internal_cam','Routine-' + str(counter) + '/' + str(photocount) + ' F' + str(counter2) + ETA)\\n            focuser.write(focus1 + focus * focusstep)\\n            take_photo('tmp/tmp.jpg')\\n            camera('/crop',msg)\\n            zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + '-' + str(focus) + \\\".jpg\\\")\\n            system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\\n    elif cam != 'external':\\n        save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\\n\\n        if cam == 'gphoto':\\n            camera('/gphoto_capture', msg)\\n        if cam in ('usb_webcam','imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\\n            take_photo('tmp/tmp.jpg')\\n        camera('/crop',msg)\\n        \\n        zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + \\\".jpg\\\")\\n        system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\\n    elif cam == 'external':\\n        camera('external_capture')\\n        save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\\n\\n    ETA = '-ETA:'+str(int((photocount/counter - 1)*(time() - starttime)))+'/'+str(int(photocount/counter*(time() - starttime)))+'s'\\n    sleep(cam_delay_after)\\n\\n    position_last = position\\n\\nzip.close()\\n\\nsave('status_internal_cam','Routine-done')\\n\\nmotorrun('rotor',position_last[0] - angle_start)\\nmotorrun('tt',position_last[1])\\n\\nsave('status_internal_cam','--READY--')\\n\\nif load_bool('routine_secondpass')==True:\\n    msg['topic'] = 'Scan done'\\n    msg['payload'] = 'Do you want to run another pass or finish this project?'\\n    msg['enabled'] = False\\n    return msg,None\\n\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 300,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"db7eea74d3bf892b\"\n            ],\n            [\n                \"0b8661103366f834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1787f08ed7070ddd\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nsave('status_internal_cam', 'Routine-stopping')\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b13dfd9f8d3711\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 395,\n        \"y\": 840,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9654deebb668e012\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 290,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8367cfa0bf5bc5df\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine\",\n        \"links\": [\n            \"210ef5246d1a8790\",\n            \"84608db962fd9932\",\n            \"8689e938.dd9e38\",\n            \"f20f2dbc.0f123\",\n            \"e9b13dfd9f8d3711\",\n            \"96bdb9417e38810f\",\n            \"fb13752beddee9f2\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 55,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"7dd287f40385922f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fb13752beddee9f2\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 895,\n        \"y\": 920,\n        \"wires\": []\n    },\n    {\n        \"id\": \"95439678bb2df2a2\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.flag = global.get('flag')\\nif (global.get('flag_pw')== true){\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"04cc2467807d2d6b\",\n                \"14f9617b5b301318\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"948a3ae4444685f2\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 610,\n        \"y\": 1260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"04cc2467807d2d6b\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 390,\n        \"y\": 1260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"12f1399b240830bf\",\n        \"type\": \"exec\",\n        \"z\": \"1613373abaf77a2c\",\n        \"command\": \" v4l2-ctl --list-formats-ext\",\n        \"addpay\": \"\",\n        \"append\": \"\",\n        \"useSpawn\": \"true\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"check cam\",\n        \"x\": 190,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"6222f781629c72e7\"\n            ],\n            [\n                \"6222f781629c72e7\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"6222f781629c72e7\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = '--READY--'\\n\\nif (msg.payload.includes('Cannot open device')){\\n    content = 'no camera found'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 350,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"e89c16809f8a5f1c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e978bf8c53d1f15a\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Settings internal cam\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ccb7da246de908d1\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview internal cam\",\n        \"info\": \"\",\n        \"x\": 110,\n        \"y\": 1160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e9566588c5e40637\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"4s/0.5\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.5\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"4\",\n        \"topic\": \"Repeat\",\n        \"payload\": \"0.2\",\n        \"payloadType\": \"str\",\n        \"x\": 80,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"14f9617b5b301318\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Take Preview Shot\",\n        \"func\": \"from time import time, sleep\\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\\nsleep(0.1)\\n\\n\\nstatus = load_str('status_internal_cam')\\ncam=load_str('camera')\\n\\n\\nif msg['flag'] == False and not 'Routine' in status:\\n    return msg\\n\\nif cam == 'external':\\n    return\\n\\nmsg['payload']=\\\"/tmp/preview.jpg?ts=\\\"+str(int(time()*10))\\n\\nif cam == 'gphoto' and status == 'no camera found':\\n    if camera('/gphoto_init') == 200:\\n        save('status_internal_cam','--READY--')\\n\\nif status!=\\\"--READY--\\\":\\n    return msg\\n\\nmsg['cropx'] = load_int('cam_cropx')\\nmsg['cropy'] = load_int('cam_cropy')\\nmsg['rotation'] = load_int('cam_rotation')\\nmsg['filepath_in'] = 'tmp/tmp.jpg'\\nmsg['filepath_out'] = 'tmp/preview.jpg'\\nmsg['filepath'] = 'tmp/tmp.jpg'\\nmsg['preview'] = True\\n\\nif cam == 'gphoto':\\n    if camera('/gphoto_test', msg) != 200:\\n        save('status_internal_cam','no camera found')\\n        return msg\\n    camera('/gphoto_preview', msg)\\n\\nif cam in ('usb_webcam', 'imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\\n    take_photo('tmp/tmp.jpg')\\n\\ncamera('/crop',msg)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 430,\n        \"y\": 1220,\n        \"wires\": [\n            [\n                \"948a3ae4444685f2\",\n                \"991b587d406d0d91\",\n                \"8f5d87ce24c40b11\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"991b587d406d0d91\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"ce9cc9d915dc6eb6\",\n        \"name\": \"preview_internal\",\n        \"order\": 1,\n        \"width\": 12,\n        \"height\": 12,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 606px;max-height: 606px\\\"/>\\n\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 620,\n        \"y\": 1220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1118d0965ff7c40b\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"projectname\",\n        \"label\": \"Projectname\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"82c8ad50ecfbc755\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ringlight\",\n        \"label\": \"Ringlight\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 660,\n        \"y\": 700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"33d94a04b96a2de0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"global.set('flag', false)\\n\\nvar file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\n\\n\\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\\n    return\\n}\\n\\nmsg.enabled = true\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c1c044f3c2139f68\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a368472a72fbc48\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview arducam with focus\",\n        \"info\": \"\",\n        \"x\": 140,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8f5d87ce24c40b11\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"preview_arducam\",\n        \"order\": 2,\n        \"width\": 10,\n        \"height\": 12,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 502px;max-height: 606px\\\"/>\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 630,\n        \"y\": 1300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"282efe64332193c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import load_str\\n\\nif load_str('camera') != 'imx519':\\n    return\\n\\nfrom Arducam import Focuser\\n\\n\\nif msg['focuser'] == True:\\n    focuser = Focuser('/dev/v4l-subdev1')\\n    focuser.write(msg['focus'])\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 1110,\n        \"y\": 1460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"64b16ef47ab6d859\",\n        \"type\": \"ui_switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"MF\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"false\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"true\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"f017f67a8d4a3750\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f017f67a8d4a3750\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"let fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\n\\nvar file = 'status_internal_cam'\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data != '--READY--'){\\n    return\\n}\\n\\nfile = 'cam_AFmode'\\ncontent = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n});\\n\\nglobal.set('AF',msg.payload)\\nmsg.enabled = false\\nif (msg.payload == false){\\n    msg.enabled = true\\n}\\nif (msg.payload == true){\\n    file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    \\n    file = 'cam_stacksize'\\n    content = String(2)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('focus1', 0)\\n    global.set('focus2', 0)\\n\\n}\\n\\n\\nmsg.focus = global.get('focus')\\nmsg.payload = 'down'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"5c39bd09.702d84\",\n                \"74521cf72050b515\",\n                \"b70e8c24ee011258\",\n                \"a2ff9dfd858821bc\",\n                \"ef62086d10d830fd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65145c939b6647e2\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"64b16ef47ab6d859\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5ea18678.975138\",\n        \"type\": \"trigger\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"20ms\",\n        \"op1\": \"\",\n        \"op2\": \"0\",\n        \"op1type\": \"pay\",\n        \"op2type\": \"str\",\n        \"duration\": \"-20\",\n        \"extend\": false,\n        \"overrideDelay\": false,\n        \"units\": \"ms\",\n        \"reset\": \"\",\n        \"bytopic\": \"all\",\n        \"topic\": \"topic\",\n        \"outputs\": 1,\n        \"x\": 730,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"fd93843e238cc9ce\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c39bd09.702d84\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F+\",\n        \"order\": 8,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: -1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-plus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dcfb5cce.0431a\",\n        \"type\": \"switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"property\": \"payload\",\n        \"propertyType\": \"msg\",\n        \"rules\": [\n            {\n                \"t\": \"eq\",\n                \"v\": \"1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"-1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"up\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"checkall\": \"true\",\n        \"repair\": false,\n        \"outputs\": 3,\n        \"x\": 550,\n        \"y\": 1420,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"8cdd0a6b.40bcd8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8cdd0a6b.40bcd8\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"reset\",\n                \"pt\": \"msg\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 560,\n        \"y\": 1480,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"e9b3837b1ffb0360\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"74521cf72050b515\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F-\",\n        \"order\": 9,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: 1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-minus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7219f62c9fdc6753\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"{{msg.payload}}\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 1130,\n        \"y\": 1420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b70e8c24ee011258\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"global\",\n        \"func\": \"if (msg.payload == 'down'){\\n    msg.enabled = false\\n    msg.payload = ' '\\n    msg.focuser = global.get('focuser')\\n    return msg\\n}\\n\\n\\nmsg.enabled = true\\n\\nsign = msg.payload\\nfocus = global.get('focus')\\nif (focus > 3000){\\n    focusstep = 5\\n}\\nelse if (focus <=3000 && focus > 2000){\\n    focusstep = 3\\n}\\nelse{\\n    focusstep = 2\\n}\\n\\n\\nfocus = focus + sign * focusstep\\n\\nsign = msg.payload\\nif (focus > 4000){\\n    distance = 6\\n    focus = 4000\\n}\\nelse if (focus > 1200 && focus <= 4000){\\n    distance = 737086 * Math.pow(focus, -1.4096)\\n}\\nelse if (focus <= 1200){\\n    distance = 999\\n    if (focus <=0){\\n       focus = 0\\n    }\\n}\\n\\n\\nglobal.set('focus', focus)\\nmsg.focus = focus\\nmsg.distance = distance\\ndistance = distance * 10\\nmsg.focuser = global.get('focuser')\\nmsg.payload = String(distance.toFixed(1)) + 'mm'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 970,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"7219f62c9fdc6753\",\n                \"282efe64332193c8\",\n                \"704a9f89089d1f25\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4a41b1e7b221486\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser f\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b3837b1ffb0360\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser t\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fd93843e238cc9ce\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"10ms\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"20\",\n        \"timeoutUnits\": \"milliseconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 850,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"b70e8c24ee011258\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"25c4138bddb77b6b\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"set\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 570,\n        \"y\": 1540,\n        \"wires\": [\n            [\n                \"95e1d239988b29e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"95e1d239988b29e0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"focus = global.get('focus')\\nfocus1 = global.get('focus1')\\nfocus2 = global.get('focus2')\\nlet fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\n    \\nif (msg.payload == false){\\n    return msg\\n}\\n\\nif (focus1 != 0 && focus2 != 0){\\n    global.set('focus1', 0)\\n    global.set('focus2', 0)\\n        file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    msg.distance1 = ' '\\n    msg.distance2 = ' '\\n    msg.enabled = false\\n    return msg\\n}\\n\\nif (focus > 4000){\\n    distance = 6\\n    focus = 4000\\n}\\nelse if (focus > 1200 && focus <= 4000){\\n    distance = 737086 * Math.pow(focus, -1.4096)\\n}\\nelse if (focus <= 1200){\\n    distance = 999.9\\n    if (focus <=0){\\n       focus = 0\\n    }\\n}\\ndistance = distance * 10\\n\\nif (focus1 == 0){\\n    global.set('focus1', focus)\\n    file = 'cam_focus1'\\n    content = String(focus)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('distance1', distance)\\n    msg.distance1 = distance.toFixed(1)\\n    msg.distance2 = 'tbd'\\n    msg.enabled = false\\n    return msg\\n}\\nif (focus1 != 0 && focus2 ==0 && focus!= focus1){\\n    global.set('focus2', focus)\\n    file = 'cam_focus2'\\n    content = String(focus)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('distance2', distance)\\n    msg.distance1 = global.get('distance1').toFixed(1)\\n    msg.distance2 = distance.toFixed(1)\\n    msg.enabled = true\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 710,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"7889245e91ddea4b\",\n                \"210ef5246d1a8790\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7889245e91ddea4b\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"{{msg.distance1}}\",\n        \"format\": \"{{msg.distance2}}\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 830,\n        \"y\": 1600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a1d29e56599da0bd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focusnumber\",\n        \"links\": [\n            \"210ef5246d1a8790\",\n            \"2dd2503d7ab0214b\",\n            \"6b94bf2295b1b31d\"\n        ],\n        \"x\": 175,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"06504f47ee1744d7\",\n                \"5f8b90ef08a7d68c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"210ef5246d1a8790\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\",\n            \"8367cfa0bf5bc5df\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 835,\n        \"y\": 1560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b6f37e23f2491639\",\n        \"type\": \"ui_switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Stack\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 6,\n        \"width\": 1,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 290,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"2d66216fee29250c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a2ff9dfd858821bc\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.payload = false\\nif (msg.enabled == false){\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"25c4138bddb77b6b\",\n                \"7889245e91ddea4b\",\n                \"4cfada2de1c5bb74\",\n                \"95e1d239988b29e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2d66216fee29250c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"file = 'cam_STmode'\\nlet fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\ncontent = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n});\\n\\nmsg.enabled = true\\nglobal.set('ST',msg.payload)\\nif (msg.payload == false){\\n    global.set('focus1',0)\\n    global.set('focus2',0)\\n    file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n        \\n    \\n    msg.enabled = false\\n}\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 430,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"25c4138bddb77b6b\",\n                \"7889245e91ddea4b\",\n                \"2dd2503d7ab0214b\",\n                \"4cfada2de1c5bb74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ef62086d10d830fd\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.payload = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 150,\n        \"y\": 1560,\n        \"wires\": [\n            [\n                \"b6f37e23f2491639\",\n                \"523019d0a2c698f5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"06504f47ee1744d7\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 12,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Stacksize:\",\n        \"format\": \"{{msg.stacksize}}\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 710,\n        \"y\": 1760,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2dd2503d7ab0214b\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\"\n        ],\n        \"x\": 535,\n        \"y\": 1620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"21306d6402225553\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.stacksize = msg.payload\\nmsg.enabled = true\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"06504f47ee1744d7\",\n                \"ca184d58f7deb4b1\",\n                \"84608db962fd9932\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e2f8fdd47bdd1b66\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stacksize\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 13,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"2\",\n        \"max\": \"20\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"21306d6402225553\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"523019d0a2c698f5\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 5,\n        \"width\": 1,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"St\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 290,\n        \"y\": 1560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dfbfe28bac5c4221\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 3,\n        \"width\": 1,\n        \"height\": 1,\n        \"name\": \"MF\",\n        \"label\": \"MF\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ca184d58f7deb4b1\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"save\",\n        \"func\": \"var file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.stacksize)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 690,\n        \"y\": 1720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"704a9f89089d1f25\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"save\",\n        \"func\": \"var file = 'cam_focus'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.focus)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1110,\n        \"y\": 1500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"5f8b90ef08a7d68c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_stacksize'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"e2f8fdd47bdd1b66\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4cfada2de1c5bb74\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"if (msg.enabled == true){\\n    msg.enabled = false\\n}\\nelse{\\n    msg.enabled = true\\n}\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 570,\n        \"y\": 1660,\n        \"wires\": [\n            [\n                \"84608db962fd9932\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"84608db962fd9932\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 675,\n        \"y\": 1660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e89c16809f8a5f1c\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"gphoto\",\n        \"func\": \"\\nfrom OpenScan import camera, save, load_str\\n\\nif load_str('camera') == 'gphoto':\\n    if camera('/gphoto_init') == 200:\\n        if camera('/gphoto_test') == 200:\\n            save('status_internal_cam','--READY--')\\n            return msg\\nif load_str('camera') == 'external':\\n    save('status_internal_cam','--READY--')\",\n        \"outputs\": 1,\n        \"x\": 470,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"917a194be245384a\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65cef204b16f8741\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable shutter\",\n        \"links\": [\n            \"2d76e5617f13cd6c\",\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"84d6b96c8ebaac96\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2aea1727dbea76ce\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropx\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"9c6b48b7b4cc4e1a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f212b44aa487945\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropy\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"c470fd0b15356206\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d1e12f51f9af0b6\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start camchk\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"12f1399b240830bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8ebd1dcb5db156ed\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Current Status:\",\n        \"format\": \"<font color= {{msg.color}} > {{msg.payload}} </font>\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"94a7aec739f9266b\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\n\\nif (data === 'no camera found'){\\n    msg.color = 'red'\\n}\\n\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"8ebd1dcb5db156ed\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2415272f42ce468c\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start status\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 55,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"94a7aec739f9266b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1e14624058e74cd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\",\n                \"107a030938cbfea9\",\n                \"84d6b96c8ebaac96\",\n                \"9c6b48b7b4cc4e1a\",\n                \"c470fd0b15356206\",\n                \"9e30e33a1520fee0\",\n                \"79ecb889f7113405\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1daf9e3a5bd5ab48\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nmsg.enabled = true\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"fb13752beddee9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d15f717d5a11002\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 300,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"e9b13dfd9f8d3711\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d14bbbb446d45e39\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview\",\n        \"links\": [\n            \"f20da2fc4978b7bf\"\n        ],\n        \"x\": 135,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"db7eea74d3bf892b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"1613373abaf77a2c\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Finish\",\n        \"cancel\": \"2nd pass\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 510,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"0b8661103366f834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0b8661103366f834\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"continue\",\n        \"func\": \"from os import system\\nfrom os.path import isfile\\n\\n\\nif msg['payload'] == '2nd pass':\\n    msg['enabled'] = True\\n    return msg,None\\n\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectcode = msg['projectcode']\\n\\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\\n\\nmsg['path'] = basepath + 'scans/' + projectcode + '.zip'\\n\\nif isfile(zippath):\\n    system('rm ' + zippath)\\n\\nreturn None, msg\",\n        \"outputs\": 2,\n        \"x\": 660,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ],\n            [\n                \"1daf9e3a5bd5ab48\",\n                \"579f2211199fd6ab\",\n                \"c433515042ba01b5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"79ecb889f7113405\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"inactive\",\n        \"func\": \"from requests import get\\nfrom OpenScan import load_int\\n\\ntimeout = load_int('timeout_ringlight')\\n\\nmsg['cmd'] = 'get'\\n\\ntry:\\n    flask = 'http://127.0.0.1:1312/ping'\\n    r = get(flask, params=msg)\\n\\n    idle = float(r.text.split(\\\":\\\")[1].split('}')[0])\\n\\n    msg['payload'] = idle\\n\\n    if idle > timeout:\\n        return msg,msg\\nexcept:\\n    pass\\n\\nreturn None,msg\",\n        \"outputs\": 2,\n        \"x\": 200,\n        \"y\": 740,\n        \"wires\": [\n            [\n                \"9e30e33a1520fee0\"\n            ],\n            [\n                \"8d7e04531c34f349\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8d7e04531c34f349\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"30\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 200,\n        \"y\": 780,\n        \"wires\": [\n            [\n                \"79ecb889f7113405\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c433515042ba01b5\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"pause\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 18,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-pause\",\n        \"payload\": \" \",\n        \"payloadType\": \"str\",\n        \"topic\": \"Scan paused\",\n        \"topicType\": \"str\",\n        \"x\": 810,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"63db399d8ac2acb6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"63db399d8ac2acb6\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"pause\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nif status == 'Routine-paused':\\n    save('status_internal_cam', 'Routine-continue')\\nelse:\\n    save('status_internal_cam', 'Routine-paused')\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 1040,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8a3fde5206ce1ae\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"name\": \"shutter\",\n        \"order\": 13,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0.1;\\n        scope.maxValue = 300;\\n        scope.stepValue = 0.1;\\n        scope.sliderName =\\\"Shutterspeed (ms)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"44c3947a9b92d32d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"87be854db758a9a6\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"name\": \"Cropy\",\n        \"order\": 16,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"\\n          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0;\\n        scope.maxValue = 100;\\n        scope.stepValue = 1;\\n        scope.sliderName = \\\"Crop Y (%)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"26f17a7f406df73c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9daea4bd57f7a00e\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"name\": \"Cropx\",\n        \"order\": 15,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 0;\\n        scope.maxValue = 100;\\n        scope.stepValue = 1;\\n        scope.sliderName = \\\"Crop x (%)\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"c2b2ab5524271123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb6ebdabaaf7d0da\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"name\": \"Photos\",\n        \"order\": 14,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<div\\n  style=\\\"overflow:hidden; position: absolute; top: 0; left: 4px; width: 98%; height: 100%;transform: translateY(-35%);\\\">\\n  <md-card>\\n    <style>\\n      .slider-container {\\n        transform: translateY(-20%);\\n        display: flex;\\n        flex-direction: column;\\n        width: 100%;\\n        overflow: hidden;\\n        margin: 0 0;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-thumb {\\n        background-color: transparent;\\n      }\\n\\n      .slider-container::-webkit-scrollbar-track {\\n        background-color: transparent;\\n      }\\n\\n      .top-row {\\n        display: flex;\\n        justify-content: space-between;\\n        align-items: center;\\n        position: relative;\\n        vertical-align: text-top;\\n        z-index: 1;\\n        top: -35%;\\n      }\\n\\n      .slider-label {\\n        text-align: left;\\n      }\\n\\n      .value-input {\\n        width: 20%;\\n        text-align: middle;\\n        transform: translateY(12%) !important;\\n      }\\n\\n      .value-input input::-webkit-inner-spin-button,\\n      .value-input input::-webkit-outer-spin-button {\\n        -webkit-appearance: none;\\n        margin: 0;\\n      }\\n\\n      .value-input input {\\n        -moz-appearance: textfield;\\n      }\\n\\n      .slider-row {\\n        position: relative;\\n        left: 5% !important;\\n        width: 70% !important;\\n        top: -100% !important;\\n        transform: translateY(-120%) !important;\\n        z-index: 1;\\n      }\\n    </style>\\n    <div class=\\\"slider-container\\\">\\n      <div class=\\\"top-row\\\">\\n        <div class=\\\"slider-label\\\">\\n          <span>{{sliderName}}</span>\\n        </div>\\n        <div class=\\\"value-input\\\" style=\\\"text-align: center;\\\">\\n          <md-input-container>\\n            <input aria-label=\\\"value\\\" type=\\\"number\\\" ng-model=\\\"sliderValue\\\" ng-change=\\\"validateAndUpdateValue(sliderValue)\\\">\\n          </md-input-container>\\n        </div>\\n      </div>\\n      <div class=\\\"slider-row\\\">\\n        <md-slider flex min=\\\"{{minValue}}\\\" max=\\\"{{maxValue}}\\\" ng-model=\\\"sliderValue\\\"          ng-change=\\\"updateSliderValue(sliderValue)\\\" aria-label=\\\"slider\\\" step=\\\"{{stepValue}}\\\">\\n        </md-slider>\\n      </div>\\n    </div>\\n</div>\\n<script>\\n  (function(scope) {\\n        scope.sliderValue = 0;\\n        scope.minValue = 10;\\n        scope.maxValue = 400;\\n        scope.stepValue = 10;\\n        scope.sliderName =\\\"Photos\\\";\\n\\n        scope.updateSliderValue = function(value) {\\n          scope.send({payload: value});\\n        };\\n\\n        scope.validateAndUpdateValue = function(value) {\\n          if (value < scope.minValue) {\\n            scope.sliderValue = scope.minValue;\\n          } else if (value > scope.maxValue) {\\n            scope.sliderValue = scope.maxValue;\\n          } else {\\n            scope.sliderValue = value;\\n          }\\n          scope.updateSliderValue(scope.sliderValue);\\n        };\\n\\n        scope.$watch('msg', function(msg) {\\n          if (msg && msg.hasOwnProperty('payload')) {\\n            scope.sliderValue = msg.payload;\\n          }\\n        });\\n    })(scope);\\n</script>\\n</md-card>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": true,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"ce28a0b5bfb0d5a1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ea54fcc2.cfcc2\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"get dirs\",\n        \"func\": \"from glob import glob\\nimport os\\nfrom zipfile import ZipFile\\nfrom datetime import datetime\\nfrom PIL import Image\\n\\ndef set_stats(stat):\\n    try:\\n        with open(directory+set[:-4]+\\\"/\\\"+stat,\\\"r\\\") as file:\\n            stat=file.read()\\n    except:\\n        stat=\\\"\\\"\\n    return stat\\n\\ntable=[]\\ndirectory=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nfor d in glob(directory+\\\"*.zip\\\"):\\n    set=os.path.basename(d)\\n\\n    try:\\n        with ZipFile(d, 'r') as f:\\n            photos = len(f.namelist())\\n        \\n            if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\\n                image = f.open(f.namelist()[int(photos/2)])\\n                img = Image.open(image)\\n                width, height = img.size\\n                width_factor = width/300\\n                height_factor = height/295\\n                if height_factor>=width_factor and height_factor > 1:\\n                    new_size=(int(width/height_factor), int(height/height_factor))\\n                    img = img.resize(new_size)\\n                elif height_factor<width_factor and width_factor > 1:\\n                    new_size=(int(width/width_factor),int(height/width_factor))\\n                    img = img.resize(new_size)\\n                img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\\n        list=[]\\n        for fi in f.filelist:\\n            list.append(f.getinfo(fi.filename).date_time)\\n        \\n        duration = str(datetime(*max(list)) - datetime(*min(list)))\\n        \\n        size = float(int(float(os.path.getsize(d))/100000))/10\\n        size_full= os.path.getsize(d)\\n        status=set_stats(\\\"status\\\")\\n        expiration=set_stats(\\\"expiration\\\")\\n        download=set_stats(\\\"download\\\")\\n    \\n        if len(download)!=0:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Download\\\":\\\"<a href=\\\"+download+\\\">RESULT</a>\\\",\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n                })\\n        else:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Size_full\\\":size_full,\\n                \\\"Duration\\\":duration,\\n\\n                })\\n    except:\\n        pass\\n\\nmsg['payload']=table\\nmsg['topic']=\\\"\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 480,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"b9a3a0f9.bcbea\",\n                \"f3662f8c7d3d7a2d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2f4c0f98.dee2\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"filelist\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"a4f09e25.02569\",\n            \"ed35109311335099\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 355,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b9a3a0f9.bcbea\",\n        \"type\": \"ui_table\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"b5fdd57b.15eda8\",\n        \"name\": \"\",\n        \"order\": 1,\n        \"width\": 13,\n        \"height\": 7,\n        \"columns\": [\n            {\n                \"field\": \"Date\",\n                \"title\": \"Date\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Name\",\n                \"title\": \"Name\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Photos\",\n                \"title\": \"Photos\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Duration\",\n                \"title\": \"ΔT\",\n                \"width\": \"60\",\n                \"align\": \"left\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Size\",\n                \"title\": \"Size\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Status\",\n                \"title\": \"Status\",\n                \"width\": \"140\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            }\n        ],\n        \"outputs\": 1,\n        \"cts\": true,\n        \"x\": 610,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"50710948.71c308\",\n                \"4082b136.dae18\",\n                \"834046a4.647938\",\n                \"0c387c0291d6c131\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"952ce286.4ffd4\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Status\",\n        \"label\": \"Status\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 60,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d4383424.7807c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"upload\",\n        \"func\": \"import os\\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\\nfrom subprocess import getoutput\\n\\nbasedir = '/home/pi/OpenScan/'\\n\\nif load_str(\\\"feedback_terms\\\")==\\\"False\\\":\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic'] = 'OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nmsg = msg['payload']\\n\\ndef upload(filelist, ulinks):\\n    pid = getoutput('pidof curl')\\n    if pid != \\\"\\\":\\n        os.system('kill ' + pid)\\n\\n    i = 0\\n    for file in filelist:\\n        link = ulinks[i]\\n        save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\\n        cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\\\"' + file + '\\\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\\n        i = i+1\\n        os.system(cmd)\\n\\n########\\nif not os.path.isfile(basedir + 'settings/token'):\\n    msg['flag'] = True\\n    save('status_cloud', 'please enter token first')\\n    return msg\\nwith open(basedir + 'settings/token', 'r') as file:\\n    token = file.read().strip('\\\\n')\\n\\n########\\nr = OpenScanCloud('getTokenInfo', {'token':token})\\n\\nif r.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    save('status_cloud', 'invalid/missing token')\\n    return None,msg\\nelif r.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\n########\\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\\n    msg['flag'] = True\\n    save('status_cloud', 'limit(s) exceeded')\\n    return msg\\n\\n########\\ntemp =  OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\\nif temp.status_code not in (200,401):\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nif temp.status_code != 401:\\n    temp = temp.json()\\n    if 'status' in temp:\\n        if temp['status'] != 'created':\\n            with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\\n                file.write(temp['status'])\\n            msg['topic'] = \\\"Project already exists\\\"\\n            msg['payload'] = \\\"Do you want to re-upload the files?\\\"\\n            return None,None,msg\\n#####\\n\\nmsg2={}\\nmsg2['token'] = token\\nmsg2['parts'] = 1\\nmsg['partslist']=[]\\n\\n#######\\nsize_to_split = load_int('osc_splitsize')\\n\\nif msg['Size_full'] > size_to_split:\\n    tempdir = basedir + 'tmp/split/'\\n    if os.path.isdir(tempdir):\\n        os.system('rm -r ' + tempdir)\\n    os.mkdir(tempdir)\\n    save('status_cloud', 'zipping files, please wait ...')\\n    cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\\n    os.system(cmd)\\n    save('status_cloud', 'zip done')\\n    list = os.listdir(tempdir)\\n    for l in list:\\n        msg['partslist'].append(tempdir + l)\\n        msg['partslist'].sort()\\n    msg2['parts']=len(msg['partslist'])\\nelse:\\n    msg['partslist'] = [basedir + 'scans/' +msg['Set']]\\n\\n#######\\nmsg2['photos'] = msg['Photos']\\nmsg2['filesize'] = msg['Size_full']\\nmsg2['project'] = msg['Set']\\n\\nr = OpenScanCloud('createProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\\n    os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\\n    file.write('prepared')\\n\\nsave('status_cloud', 'uploading')\\nupload(msg['partslist'], msg1['ulink'])\\n\\nr = OpenScanCloud('startProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Upload failed'\\n    msg['payload'] = 'please try again'\\n    save('status_cloud', 'upload failed')\\n    return None,msg\\n\\nsave('status_cloud', 'uploaded')\\n\\nsave('status_cloud', 'project started')\\n\\ntry:\\n    os.system('rm -r ' + tempdir)\\nexcept:\\n    pass\\n\\nreturn msg\",\n        \"outputs\": 3,\n        \"x\": 530,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"9a132ab1.b21658\"\n            ],\n            [\n                \"3d16b3789632784d\",\n                \"9a132ab1.b21658\"\n            ],\n            [\n                \"c1708277f79e61de\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50710948.71c308\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"set\",\n                \"pt\": \"global\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 750,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ada1b6f7cccc9344\",\n                \"85839a17fb7b58b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"834046a4.647938\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Set\",\n        \"label\": \"Set:\",\n        \"format\": \"{{msg.payload.Name}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9a132ab1.b21658\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 780,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"8689e938.dd9e38\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3c67e97b.9d19a6\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable\",\n        \"func\": \"if (global.get('flag') === false){\\n    msg.enabled = false\\n    msg.color=\\\"white\\\"\\n}\\nelse{\\n    msg.enabled = true\\n    msg.color=\\\"red\\\"\\n    \\n}\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 130,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"7a93d1e18254685c\",\n                \"e434ef42bd6b92e8\",\n                \"d5d840183025d91b\",\n                \"ab9e90ab5a53a0dd\",\n                \"478994f671a3907d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bfc01f26.c32cf\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 420,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"f20f2dbc.0f123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b33d604c.5f1a6\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable cloud\",\n        \"links\": [\n            \"4082b136.dae18\",\n            \"8689e938.dd9e38\",\n            \"bd75f33b8a57c522\",\n            \"e9b13dfd9f8d3711\",\n            \"f20f2dbc.0f123\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 35,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"3c67e97b.9d19a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f6bd1a04.470838\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"payload\",\n                \"pt\": \"msg\",\n                \"to\": \"set\",\n                \"tot\": \"global\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 410,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"d4383424.7807c8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4082b136.dae18\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"b33d604c.5f1a6\",\n            \"87574a42938afec4\"\n        ],\n        \"x\": 715,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f20f2dbc.0f123\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 525,\n        \"y\": 520,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8689e938.dd9e38\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 875,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15de0ebb.616d61\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a7d89487.ee8858\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a7d89487.ee8858\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\ntry:\\n    os.remove(dir+msg['Set'])\\n    shutil.rmtree(dir+msg['Set'][:-4])\\nexcept:\\n    pass\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4f09e25.02569\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"c20357dd.374108\",\n            \"e9aab326.a6896\",\n            \"edd22cc7.befe1\",\n            \"19b81967.49db87\",\n            \"8ee1b3bb.7b0b3\",\n            \"d5246b3cc796afc6\"\n        ],\n        \"x\": 775,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7a93d1e18254685c\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"92c98e6ce7cd25f9\"\n        ],\n        \"x\": 235,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d99c601c9881680\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"refresh\",\n        \"func\": \"from time import sleep\\nimport os\\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\n\\nif load_bool(\\\"terms\\\")==False:\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic']='OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nsave('status_cloud','refreshing')\\ntoken = load_str('token')\\n\\ntest = OpenScanCloud('getTokenInfo',{'token':token})\\nif test.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    return None,msg\\nelif test.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nstats = test.json()\\nfor i in stats:\\n    save('osc_'+i, stats[i])\\n    pass\\n\\nmsg={}\\nprojects = []\\nfor i in os.listdir(basepath):\\n    if i == 'preview':\\n        continue\\n    if os.path.isdir(basepath + i):\\n        if os.path.isfile(basepath + i + '/status'):\\n            with open(basepath + i + '/status', 'r') as file:\\n                status = file.read().strip('\\\\n')\\n                if status in ['expired', 'processing done', 'processing failed']:\\n                    continue\\n        projects.append(i)\\n\\nfor p in projects:\\n    r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\\n    if r.status_code == 200:\\n        answer = r.json()\\n        if answer == {}:\\n            os.system('rm -r ' + basepath + p)\\n        else:\\n            with open(basepath + p + '/status', 'w+') as file:\\n                file.write(answer['status'])\\n            with open(basepath + p + '/download', 'w+') as file:\\n                file.write(answer['dlink'])\\n\\nmsg['list'] = projects\\nsleep(0.5)\\nsave('status_cloud','ready')\\nreturn msg, None\\n\",\n        \"outputs\": 2,\n        \"x\": 320,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\",\n                \"b42e061fb1f1f3d7\"\n            ],\n            [\n                \"6434e713f088012b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"372e95797a3f2f3b\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"limit :)\",\n        \"func\": \"from time import sleep\\n\\nmsg2={}\\nmsg2['enabled'] = True\\n\\nmsg['enabled'] = False\\nnode.send(msg)\\n\\nwait = 15\\n\\nfor i in range (wait):\\n    msg['text'] = ' ('+ str(wait - i)+')'\\n    node.send(msg)\\n\\nmsg['enabled'] = True\\nmsg['text']=\\\"\\\"\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 90,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"573edbfdb7500ddc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573edbfdb7500ddc\",\n        \"type\": \"delay\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dacb1f078b624e10\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"c8d65cc7c2ff7c36\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92c98e6ce7cd25f9\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"7a93d1e18254685c\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 35,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3d16b3789632784d\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 770,\n        \"y\": 480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6434e713f088012b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 470,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8d65cc7c2ff7c36\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\nfor i in os.listdir(dir):\\n    if os.path.isdir(dir + i):\\n        shutil.rmtree(dir + i)\\n    else:\\n        os.remove(dir + i)\\n\\nos.mkdir(dir + \\\"preview\\\")\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4e9a4bd79b4221f\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dacb1f078b624e10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2806bf08ea21216d\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.Set=global.get('set')['Set']\\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"15de0ebb.616d61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"61990987acd0f263\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 45,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"51579603bce21e98\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e8e488a6dd5d0b33\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"Download\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href={{msg.download}}>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 880,\n        \"y\": 260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0c387c0291d6c131\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.download = '/scans/' + String(msg.payload.Set)\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"e8e488a6dd5d0b33\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e5f38b4a07a5e278\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 655,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"834046a4.647938\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e434ef42bd6b92e8\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"upload2\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">upload</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f6bd1a04.470838\",\n                \"bfc01f26.c32cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c46e10b9c201913e\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"refresh\",\n        \"order\": 1,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">refresh{{msg.text}}</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 160,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"372e95797a3f2f3b\",\n                \"4d99c601c9881680\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d5d840183025d91b\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del set\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;color:red; width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"2806bf08ea21216d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab9e90ab5a53a0dd\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del \",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete all</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"f4e9a4bd79b4221f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"478994f671a3907d\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"combine\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">combine</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"51bfd0fb7b1d292e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"189c1eed09624a7b\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"combine = global.get('combine')\\ncombine_set = global.get('set').Set\\n\\nif (combine === true && global.get('combine_set') !== combine_set){\\n    msg.set1 = global.get('combine_set')\\n    msg.set2 = combine_set\\n    global.set('combine', false)\\n    msg.topic = 'Combine the following two sets:'\\n    msg.payload = msg.set1 + '<br>' + msg.set2 + '<br>FILES WILL BE MERGED INTO ON FILE!'\\n    return msg\\n}\\nglobal.set('combine_set' , combine_set)\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"1493398979a63775\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51bfd0fb7b1d292e\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"global.set('combine', true)\\ncombine_set = global.get('set').Set\\nmsg.topic = 'Merge two sets into one (can not be undone)!'\\nmsg.payload = combine_set\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 420,\n        \"y\": 620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"da325be8e74179be\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"from os.path import getsize\\nfrom shutil import copy\\nfrom os import rename, remove\\nimport zipfile as z\\nfrom OpenScan import save\\n\\nfrom time import sleep\\n\\nif msg['payload'] != 'OK':\\n    return\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\ntmp1 = basepath + msg['set1']\\ntmp2 = basepath + msg['set2']\\n\\nif getsize(tmp1) > getsize(tmp2):\\n    set1 = tmp1\\n    set2 = tmp2\\nelse:\\n    set1 = tmp2\\n    set2 = tmp1\\n\\nzips = [set1, set2]\\n\\nwith z.ZipFile(set1, 'a') as z1:\\n    z2 = z.ZipFile(set2, 'r')\\n    i = 0\\n    for n in z2.namelist():\\n        i += 1\\n        n2 = n\\n        save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\\n        while 'X'+n in z1.namelist():\\n            n = 'X' + n\\n        z1.writestr('X'+n, z2.open(n2).read())\\nsave('status_cloud','ready')\\n\\nos.rename(set1, set1[:-4] + 'X.zip')\\nos.remove(set2)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 560,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"ed35109311335099\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ed35109311335099\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"2f4c0f98.dee2\"\n        ],\n        \"x\": 655,\n        \"y\": 660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1493398979a63775\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Combine\",\n        \"x\": 420,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"da325be8e74179be\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ada1b6f7cccc9344\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"6dd356510c446cf4\"\n        ],\n        \"x\": 835,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6dd356510c446cf4\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"links\": [\n            \"ada1b6f7cccc9344\"\n        ],\n        \"x\": 175,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"189c1eed09624a7b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b42e061fb1f1f3d7\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"397ab7f44b893c89\"\n        ],\n        \"x\": 435,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b99505440832439f\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"diskspace\",\n        \"func\": \"from subprocess import getoutput\\nfrom OpenScan import load_int\\n\\ndiskspace_threshold = load_int('diskspace_threshold')\\n\\ndiskspace = getoutput('df -h / | awk \\\"{print $5}\\\"').split('\\\\n')[1]\\n\\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\\n\\n\\nif available < diskspace_threshold:\\n    msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \\n    msg['payload'] = 'Please delete some/all locally stored files.'\\n    msg['color'] = 'red'\\n    return msg\\n\",\n        \"outputs\": 1,\n        \"x\": 800,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"92047434f8e9f927\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92047434f8e9f927\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 950,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f3662f8c7d3d7a2d\",\n        \"type\": \"delay\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"minute\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 650,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"b99505440832439f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51579603bce21e98\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"read\",\n        \"func\": \"from OpenScan import load_str\\nfrom os import listdir, path\\n\\nstatus = load_str('status_cloud')\\n\\nif status[0:9] == 'uploading':\\n    progress = load_str('status_uploadprogress')[-6:]\\n    if progress[-1:] == '%':\\n        status = status + ' (' + progress + ')'\\n\\nif status[0:7] == 'zipping':\\n    path1 = '/home/pi/OpenScan/tmp/split/'\\n    files = listdir(path1)\\n    size1 = 0\\n    for file in files:\\n        size1 += path.getsize(path1+file)\\n    size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\\n    \\n    status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\\n\\nmsg['status'] = status\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 130,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"952ce286.4ffd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a5baae623355f9d\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"preview\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 6,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 301px;max-height: 301px\\\"/>\\n\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 1020,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"85839a17fb7b58b9\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"preview\",\n        \"func\": \"from time import time\\nimport os\\n\\npath = '/home/pi/OpenScan/scans/preview/'\\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\\n\\nmsg['payload']=\\\"/scans/preview/\\\" + image +\\\"?ts=\\\"+str(int(time()*10))\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 880,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"9a5baae623355f9d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"45058bfcf047e8cc\",\n        \"type\": \"inject\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 100,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c1708277f79e61de\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Yes\",\n        \"cancel\": \"No\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"resetProject?\",\n        \"x\": 790,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"98a45dd77f31c8b4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"98a45dd77f31c8b4\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"resetProject\",\n        \"func\": \"from OpenScan import OpenScanCloud\\n\\nif msg['payload'] == \\\"No\\\":\\n    return\\n\\nwith open('/home/pi/OpenScan/settings/token', 'r') as file:\\n    token = file.read().strip('\\\\n')\\n\\nOpenScanCloud('resetProject', {'token':token, 'project':msg['Set']})\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 950,\n        \"y\": 520,\n        \"wires\": [\n            [\n                \"f6bd1a04.470838\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"40dee936a9abac0d\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"SSH\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dc354c54078ca607\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4fd9bb53fdb51a25\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Samba\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"test2\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"b0aa8ffae5a3578a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dc354c54078ca607\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ssh\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('ssh'):\\n    save('ssh', state)\\n\\nif state == True:\\n    os.system('/etc/init.d/ssh start')\\nelse:\\n    os.system('/etc/init.d/ssh stop')\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"52858b4eceacc902\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Terms Of Use\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 120,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"f99ec8781a33ec7d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"595153429adef33b\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Network Settings</h1> <p><strong>Hostname</strong></p> <p style=\\\"margin-left: 20px;\\\">The device can be accessed through any browser in the same network. Therefore, you can either enter the device&apos;s IP address or the given hostname. The standard name is &apos;openscan&apos; but it is highly recommended to change the name, when using multiple devices (e.g. &apos;openscan1&apos;, &apos;openscan2&apos; ...)</p> <p><strong>Select Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: <a href=\\\"%5C%22https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>ISO 3166 Country Code on Wikipedia</a></p> <p><strong>Search Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">You can manually refresh the list of available networks by pressing this button.</p> <p><strong>Reset Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see <a href=\\\"%5C%22https://www.glennklockwood.com/sysadmin-howtos/rpi-headless-boot.html%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>glennklockwood.com</a> for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 590,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dc39bd847d16ded\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Agree\",\n        \"cancel\": \"Disagree\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 410,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"5f849178998d9082\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"02858034e17b827f\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"General\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>General Settings</h1> <p><strong>Terms Of Use</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.</p> <p><strong>SSH</strong></p> <p style=\\\"margin-left: 20px;\\\">SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.</p> <p style=\\\"margin-left: 20px;\\\">If you want to use it, the default user is <strong>pi</strong>, password: <strong>raspberry. Please change the password&nbsp;</strong><strong>immediately.</strong>&nbsp;</p> <p><strong>Samba</strong></p> <p style=\\\"margin-left: 20px;\\\">Samba s a network local file sharing server, which allows accessing the Raspberry Pi&apos;s file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">You can access the Raspberry Pis file system by inserting the following line into your Windows explorer:&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">\\\\\\\\OpenScan/PiShare/OpenScan/scans/</p> <p style=\\\"margin-left: 20px;\\\">username: pi, password: raspberry</p> <p style=\\\"margin-left: 20px;\\\"><strong>Please deactivate the local file sharing if you do not intend to use it</strong></p> <p><strong>Advanced Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">Enable a ton of additional settings, which should be changed only if you know what you are doing ;)</p> <p><strong>Model</strong></p> <p style=\\\"margin-left: 20px;\\\">Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.</p> <p><strong>Camera</strong></p> <p style=\\\"margin-left: 20px;\\\">A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.</p> <p style=\\\"margin-left: 20px;\\\"><u>DSLR (gphoto)</u> - connect a wide range of DSLR cameras to the device through USB. See <a href=\\\"http://www.gphoto.org/proj/libgphoto2/support.php\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">GPhoto</a> for a full list of supported devices.</p> <p style=\\\"margin-left: 20px;\\\"><u>External camera</u> - triggering any camera through an isolated GPIO signal on the front side of the pi shield.</p> <p><strong>Shutdown/Reboot</strong></p> <p style=\\\"margin-left: 20px;\\\">Always use the shutdown button before you power off your Raspberry Pi.</p> <p><strong>Restore Default Settings</strong></p> <p style=\\\"margin-left: 20px;\\\">In case you want to restore the default settings</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 740,\n        \"y\": 240,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"675d4933a44ae6b5\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Pinout\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1><span style=\\\"color: rgb(226, 80, 65);\\\">Pinout</span></h1> <p><span style=\\\"color: rgb(226, 80, 65);\\\"><strong><span style=\\\"font-size: 19px;\\\">ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!</span></strong></span></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b0aa8ffae5a3578a\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"smb\",\n        \"func\": \"from OpenScan import load_bool, save\\n\\nstate = msg['payload']\\n\\nif state != load_bool('smb'):\\n    save('smb', state)\\nif state == True:\\n    os.system('/etc/init.d/smbd start')\\nelse:\\n    os.system('/etc/init.d/smbd stop')\\n\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 380,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cc3cb10f2ea3f8b8\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"blink Light1\",\n        \"func\": \"import RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nfrom OpenScan import ringlight\\nfrom time import sleep\\n\\ndelay = 0.1\\nringlight(2,False)\\n\\nfor i in range (5):\\n    ringlight(1,True)\\n    sleep(delay)\\n    ringlight(1,False)\\n    sleep(delay)\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d114f4d4d7f31981\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"func\": \"import os\\nfrom time import sleep\\nsleep(1.5)\\nimport RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nos.system('reboot -h')\\n\",\n        \"outputs\": 1,\n        \"x\": 270,\n        \"y\": 720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"79181ad3b56d5c62\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Model\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d81bd138733c410\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Camera\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 840,\n        \"y\": 420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"80b579a4220e5c23\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 8,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Please Select\",\n                \"value\": \"None\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Mini\",\n                \"value\": \"OSMini\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Classic\",\n                \"value\": \"OSClassic\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"896242c5a7e50fa7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a2c1dba3e67be015\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 10,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Pi Cam v1 - 5mp\",\n                \"value\": \"ov5647\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam v2 - 8mp\",\n                \"value\": \"imx219\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam HQ - 12.3mp\",\n                \"value\": \"imx477\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Arducam IMX519 - 16mp\",\n                \"value\": \"imx519\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 a\",\n                \"value\": \"imx290a\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 b\",\n                \"value\": \"imx290b\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX378\",\n                \"value\": \"imx378\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OV9281\",\n                \"value\": \"ov9281\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"DSLR (gphoto)\",\n                \"value\": \"gphoto\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"USB Webcam\",\n                \"value\": \"usb_webcam\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"External Camera\",\n                \"value\": \"external\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"4058a31e942e8f95\",\n                \"6d68cccec646e0a0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9cf5d56263caada7\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Motor\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Motor Settings</h1> <p><strong>Turntable Mode</strong></p> <p style=\\\"margin-left: 20px;\\\">Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.</p> <p><strong>Rotor - Start Angle, Min and Max Angle</strong></p> <p style=\\\"margin-left: 20px;\\\">Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0&deg; corresponds to the horizontal (natural) orientation.</p> <p style=\\\"margin-left: 20px;\\\">After that, the device will equally space the image positions between angle min and angle max.</p> <p><strong>Rotor/Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>Steps per rotation</u> - &nbsp;defines the number of steps it takes to move the axis 360&deg;. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)</p> <p style=\\\"margin-left: 20px;\\\"><u>Delay</u> - time in microseconds between each step of the motor. Lower this value if the movement is too fast</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration</u> - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.</p> <p style=\\\"margin-left: 20px;\\\"><u>Acceleration ramp</u> - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.</p> <p style=\\\"margin-left: 20px;\\\"><u>Manual Angle</u> - Defines the degree value for the manual movement through the arrow buttons in the scan menu</p> <p style=\\\"margin-left: 20px;\\\"><u>Direction</u> - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180&deg; (BUT MAKE SURE TO POWER OFF THE DEVICE!)</p> <p style=\\\"margin-left: 20px;\\\"><a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\"></a></p> <p><br></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 430,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"72238e6a01d1152c\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"camera\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Camera Settings</h1> <p><strong>Jpeg quality</strong></p> <p style=\\\"margin-left: 20px;\\\">Value in percent, which usually does not need to be changed.</p> <p><strong>Downscale Preview</strong></p> <p style=\\\"margin-left: 20px;\\\">The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update</p> <p><strong>Image Rotation</strong></p> <p style=\\\"margin-left: 20px;\\\">Change the image rotation, if needed.</p> <p><strong>Timeout</strong></p> <p style=\\\"margin-left: 20px;\\\">Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.</p> <p><strong>Delay Before/After</strong></p> <p style=\\\"margin-left: 20px;\\\">A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.</p> <p><strong>AWBG, Gain, Contrast, Saturation</strong></p> <p style=\\\"margin-left: 20px;\\\">Under most circumstances, you do not need to touch these values.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"15a0a2f431ce55c3\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"General Settings\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"87a403b9a09aa38d\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Network\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"896242c5a7e50fa7\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstate = msg['payload']\\nmsg['state'] = state\\n\\nif state != load_str('model'):\\n    save('model', state)\\n    if state == 'OSMini':\\n        save('rotor_stepsperrotation',48000)\\n        save('cam_rotation',90)\\n        save('rotor_anglemin',-70)\\n        save('rotor_anglemax',20)\\n        \\n\\n    if state == 'OSClassic':\\n        save('rotor_stepsperrotation',17067)\\n        save('cam_rotation',0)\\n        save('rotor_anglemin',-30)\\n        save('rotor_anglemax',30)\\n\\nif state == \\\"OSMini\\\":\\n    msg['crop2'] = 'Crop X (%)'\\n    msg['crop1'] = 'Crop Y (%)'\\nelif state == \\\"OSClassic\\\":\\n    msg['crop1'] = 'Crop X (%)'\\n    msg['crop2'] = 'Crop Y (%)'\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"f358de1e64b491bb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4058a31e942e8f95\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"camera\",\n        \"func\": \"from OpenScan import load_str, save\\nfrom json import load\\nstate = msg['payload']\\nstate_old = load_str('camera')\\n\\nif state_old != state:\\n    save('camera',state)\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 540,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"34b685aff2080d31\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c833f6243a059d83\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"more sets\",\n        \"label\": \"Advanced Settings\",\n        \"tooltip\": \"\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 5,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"8be8015931c663cc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"15fd1c9e5610cb85\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"more sets\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    msg['payload'] = False\\n    return None,msg\\n    \\nsave('advanced_settings', True)\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 820,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"62cd775a1c02dac8\"\n            ],\n            [\n                \"c833f6243a059d83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"74c5c7cd2681045b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"load camera&model\",\n        \"func\": \"from OpenScan import load_str, load_bool\\n\\nmodel = load_str('model')\\ncamera = load_str('camera')\\nupdate = load_bool('updateable')\\nmsg['model'] = model\\nmsg['camera'] = camera\\nmsg2 = {}\\nmsg3 = {}\\nmsg4 = {}\\n\\nif camera in ('imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','gphoto'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Arducam\\\"],\\\"show\\\":[\\\"Scan_Settings\\\",\\\"Scan_Picamera\\\"]}}\\nelif camera in ('imx519'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Picamera\\\"],\\\"show\\\":[\\\"Scan_Settings\\\",\\\"Scan_Arducam\\\"]}}\\nelif camera in ('external'):\\n    msg['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"Scan_Arducam\\\",\\\"Scan_Picamera\\\"],\\\"show\\\":[\\\"Scan_Settings\\\"]}}\\n\\n\\nif model == 'None' or model == '' or camera == 'None' or camera == '':\\n    msg2['payload']={\\\"tabs\\\": {\\\"hide\\\": [\\\"Scan\\\", \\\"Files&Cloud\\\",\\\"Settings\\\",\\\"Update & Info\\\"]}}\\n    msg3['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"OpenScan_Home\\\"],\\\"show\\\":[\\\"OpenScan_Initialize\\\"]}}\\nelse:\\n    msg2['payload']={\\\"tabs\\\": {\\\"show\\\": [\\\"Scan\\\", \\\"Files&Cloud\\\",\\\"Settings\\\",\\\"Update & Info\\\"]},\\\"hide\\\":{}}\\n    msg3['payload'] = {\\\"group\\\":{\\\"show\\\":[\\\"OpenScan_Home\\\"],\\\"hide\\\":[\\\"OpenScan_Initialize\\\"]}}\\n\\nif update == True:\\n    msg4['payload'] = {\\\"group\\\":{\\\"show\\\":[\\\"OpenScan_Update\\\"]}}\\nelif update == False:\\n    msg4['payload'] = {\\\"group\\\":{\\\"hide\\\":[\\\"OpenScan_Update\\\"]}}\\n\\nreturn msg,msg2,msg3,msg4\",\n        \"outputs\": 4,\n        \"x\": 340,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ],\n            [\n                \"b4db790aad28ba39\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b4db790aad28ba39\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"change visibility\",\n        \"events\": \"all\",\n        \"x\": 600,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"eb8ccf2786ea3d63\",\n        \"type\": \"inject\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"1s_repeater\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"2\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 150,\n        \"y\": 40,\n        \"wires\": [\n            [\n                \"74c5c7cd2681045b\",\n                \"9b756a1f9b0e7317\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b756a1f9b0e7317\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"load advanced\",\n        \"func\": \"from OpenScan import load_bool\\n\\nif load_bool('advanced_settings') == False:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\"]}}\\nelse:\\n    msg['payload']={\\\"group\\\":{\\\"hide\\\":[],\\\"show\\\":[\\\"Settings_General\\\",\\\"Settings_Network\\\",\\\"Settings_OpenScanCloud\\\",\\\"Settings_Camera\\\",\\\"Settings_Motor\\\",\\\"Settings_Pinout\\\",]}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 320,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"b4db790aad28ba39\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ca4afadb5b21751f\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Info Texts\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f393400.d87dcc\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"check ip address\",\n        \"func\": \"import socket\\nimport subprocess\\n\\ntestIP = \\\"8.8.8.8\\\"\\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\\ns.connect((testIP, 0))\\nipaddr = s.getsockname()[0]\\nhost = socket.gethostname()\\n\\nmsg['ip']=ipaddr\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 410,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"bb789eed.9f73c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bb789eed.9f73c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Your local IP:\",\n        \"format\": \"{{msg.ip}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 590,\n        \"y\": 1060,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2a0f9919.4c9a86\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OpenScanCloud\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 1240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"27c6b221c90ed9e1\",\n        \"type\": \"exec\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"command\": \"iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\\\"//g;s/^ *//;s/ *$//'\",\n        \"addpay\": false,\n        \"append\": \"\",\n        \"useSpawn\": \"false\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"scan\",\n        \"x\": 250,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"b05cf92302a5c112\",\n                \"f393400.d87dcc\"\n            ],\n            [\n                \"e9677b85856b5873\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"b05cf92302a5c112\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"WIFI\",\n        \"func\": \"msg['options']=[]\\n\\nfor i in msg['payload'].split('\\\\n'):\\n    if i not in msg['options'] and i!=\\\"\\\":\\n        msg['options'].append(i)\\n        \\nif len(msg['options']) != 0:\\n    msg['enabled']=True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 370,\n        \"y\": 1020,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da5ddaf4cc25b8c8\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"search\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Search Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\",\n                \"51521bc6eb44cde5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"59c9f67283ba1709\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [],\n        \"payload\": \"\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"className\": \"\",\n        \"x\": 520,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"2bb52656f9554dab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2d7d6a730f7dca6\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Reset Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)\",\n        \"payloadType\": \"str\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"78985ac6d3bcdf60\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c3b8faac9ebb2c80\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"func\": \"from time import sleep\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('wpa_cli -i wlan0 reconfigure')\\nsleep(3)\\nos.system('systemctl restart nodered')\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 440,\n        \"y\": 1140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"78985ac6d3bcdf60\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 270,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"c3b8faac9ebb2c80\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f7f49b12c2d2572\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"add Wifi\",\n        \"func\": \"from time import sleep\\nsleep(0.1)\\n\\nos.system('wpa_cli -i wlan0 reconfigure')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 1320,\n        \"y\": 1000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ebcc98685059b9d4\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"password\",\n        \"x\": 780,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"68204a14528ab842\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"68204a14528ab842\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['password'] = msg['payload']\\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: <a href=\\\"https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\\\">Wikipedia</a>)'\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 910,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"852edf901bdec9c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"852edf901bdec9c5\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Save\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"country\",\n        \"x\": 1040,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"1b09d634e3d9357b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1b09d634e3d9357b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"modWPA\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nif len(msg['payload'])!=2:\\n    msg['payload'] = 'invalid country code'\\n    return msg,None\\n\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\\n\\ncode = msg['payload'].upper()\\nssid = msg['ssid']\\npassword = msg['password']\\n\\nif len(code) != 2:\\n    msg['topic'] = 'ERROR'\\n    msg['payload'] = 'invalid country code (see  ISO 3166-1 alpha-2)'\\n    return msg\\n\\nwith open(wpa_dir, 'r') as file:\\n    for i in file.readlines():\\n        if 'country=' in i:\\n            code_old=i.split('country=')[1][0:2]\\n            break\\n\\nwith open(wpa_dir, 'r') as file:\\n    wpa = file.read()\\n    if ssid in wpa:\\n        msg['topic'] = 'ERROR'\\n        msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\\n        return msg\\n    wpa=wpa.replace('country=' + code_old, 'country=' + code)\\n    wpa=wpa + '\\\\nnetwork={\\\\n priority=10\\\\n ssid=\\\"'+ssid+'\\\"\\\\n psk=\\\"'+password+'\\\"\\\\n}\\\\n'\\n\\nwith open(temp_dir,'w+') as file:\\n    file.write(wpa)\\nos.system('mv '+temp_dir + ' ' + wpa_dir)\\n\\nmsg['topic'] = 'Updating Wifi'\\nmsg['payload'] = 'reconnecting might take a moment'\\nreturn msg,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 1180,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ],\n            [\n                \"4f7f49b12c2d2572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"03732a7d3b0c95aa\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1330,\n        \"y\": 960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e97d17c6590138e2\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Cloud-settings\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>OpenScanCloud</h1> <p style=\\\"margin-left: 20px;\\\">OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at <a href=\\\"%5C%22https://www.buymeacoffee.com/OpenScan%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>BuyMeACoffee</a>.</p> <p style=\\\"margin-left: 20px;\\\">The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.</p> <p><strong>Register</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.</p> <p><strong>Enter Token</strong></p> <p style=\\\"margin-left: 20px;\\\">Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Token</strong></p> <p style=\\\"margin-left: 20px;\\\">A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p><strong>Credit (GB)</strong></p> <p style=\\\"margin-left: 20px;\\\">Each token comes with a given amount of &apos;credit&apos; which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers.&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">IMPORTANT: The credit can be increased at any time by sending a (nice) mail to <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 620,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f7bf47e3eec6d736\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Number of Photos:\",\n        \"format\": \"{{msg.limit_photos}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b52d91c628b151a4\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Max. Filesize (GB):\",\n        \"format\": \"{{msg.limit_filesize}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 1420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1969c709ef2fd1d5\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Credit (GB):\",\n        \"format\": \"{{msg.credit}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 1460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"88e92b621d2a3394\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"save\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import save, OpenScanCloud\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return None,msg\\n\\ntry:\\n    r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\\n    if r.status_code != 200:\\n        msg['payload'] = 'Could not verify token'\\n        return msg \\n    \\n    msg1 = r.json()\\n    \\n    save('osc_credit',msg1['credit'])\\n    save('osc_limit_filesize',msg1['limit_filesize'])\\n    save('osc_limit_photos',msg1['limit_photos'])\\n    msg1['enabled'] = True\\nexcept:\\n    pass\\n\\nsave('token',msg['token'])\\n    \\nmsg['payload'] = 'Token verified and saved'\\nreturn msg, msg1\",\n        \"outputs\": 2,\n        \"x\": 750,\n        \"y\": 1320,\n        \"wires\": [\n            [\n                \"76acd48a511a5e3e\",\n                \"b01581296b94dfcd\"\n            ],\n            [\n                \"9c51aa678f16980f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"76acd48a511a5e3e\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 890,\n        \"y\": 1280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"5f50ed3f6ba37cef\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Token\",\n        \"tooltip\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 350,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"cb62d30728af2968\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cb62d30728af2968\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Save?\",\n        \"func\": \"msg['token'] = msg['payload']\\n\\nif len(msg['payload'])>=14:\\n    \\n    msg[\\\"payload\\\"]='Save and verify token: ' + msg['payload']\\n    return msg\\nelse:\\n    return None,msg\",\n        \"outputs\": 2,\n        \"x\": 470,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"94e503dd2e64d903\"\n            ],\n            [\n                \"d859bb39914d4999\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0dd01eef6e70059e\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"text\",\n        \"func\": \"from OpenScan import load_str\\n\\ntoken = load_str('token')[0:8]\\nmsg['payload']= token + '...'\\nif len(token)==0:\\n    msg['payload']=\\\"enter token\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"5f50ed3f6ba37cef\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"788fabff98c7973c\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"token\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"b01581296b94dfcd\",\n            \"d859bb39914d4999\"\n        ],\n        \"x\": 75,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"0dd01eef6e70059e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"94e503dd2e64d903\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 610,\n        \"y\": 1320,\n        \"wires\": [\n            [\n                \"88e92b621d2a3394\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d859bb39914d4999\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"788fabff98c7973c\"\n        ],\n        \"x\": 555,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9c51aa678f16980f\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"links\": [\n            \"5d267acc10020091\",\n            \"397ab7f44b893c89\"\n        ],\n        \"x\": 835,\n        \"y\": 1360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"397ab7f44b893c89\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSCparameters\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"9c51aa678f16980f\",\n            \"b42e061fb1f1f3d7\"\n        ],\n        \"x\": 75,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"a7fd00943edc380b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b01581296b94dfcd\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"links\": [\n            \"788fabff98c7973c\",\n            \"5d267acc10020091\"\n        ],\n        \"x\": 835,\n        \"y\": 1320,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bf6d941ad307ce22\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 250,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"f22dfef37d5de773\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f22dfef37d5de773\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import re\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmail = msg['payload']\\nemail_regex = re.compile(r\\\"[^@]+@[^@]+\\\\.[^@]+\\\")\\n\\nif email_regex.match(mail) != None:\\n    msg['mail'] = mail\\n    msg['topic'] = 'OpenScanCloud Registration (2/3)'\\n    msg['payload'] = 'Enter your first name'\\n    return msg\\nmsg['payload'] = 'invalid input'\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 390,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"54602ee49ca022e7\"\n            ],\n            [\n                \"1505f3e72f971081\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1505f3e72f971081\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1560,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"54602ee49ca022e7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 530,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"f9efcb87b74abbd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"510dbe4d76253bd6\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"SUBMIT\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 810,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"600b2306caed1640\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"600b2306caed1640\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import OpenScanCloud\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['lastname'] = msg['payload']\\n\\nmsg2 = {}\\n\\nfor i in ['forename','lastname','mail']:\\n    msg2[i] = msg[i]\\n\\nr = OpenScanCloud('requestToken',msg2)\\n\\nstatus = r.status_code\\n\\nmsg['topic'] = 'OpenScanCloud Registration - Success'\\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\\n\\nif status != 200:\\n    msg['topic'] = 'OpenScanCloud Registration - Failed'\\n    msg['payload'] = 'Registration failed, please try again.'\\n\\nmsg['status'] = status\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 950,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"bbad1ab5f8f63fb7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d34cd203725bac15\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Register\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Register\",\n        \"tooltip\": \"testtesttest\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Please enter your email address:\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Requesting an OpenScanCloud Token\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"bf6d941ad307ce22\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bbad1ab5f8f63fb7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1090,\n        \"y\": 1520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a7fd00943edc380b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import load_int\\n\\nmsg = {}\\n\\ntry:\\n    msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\\n    msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\\n    msg['limit_photos'] = load_int('osc_limit_photos')\\n    return msg\\nexcept:\\n    pass\",\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"f7bf47e3eec6d736\",\n                \"b52d91c628b151a4\",\n                \"1969c709ef2fd1d5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"124459147143ec6a\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Motor\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 1600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dbd62b91a6c9c412\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"info\": \"\",\n        \"x\": 90,\n        \"y\": 2240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"842b6fe016087ce3\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Pinout\",\n        \"info\": \"\",\n        \"x\": 110,\n        \"y\": 2860,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8c1a92f2dcc976c7\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Rotor_delay (ms)\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 1840,\n        \"wires\": [\n            [\n                \"bb54bbdae6690576\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2647111c06f2055d\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt delay\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 27,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"0.2\",\n        \"step\": \"0.005\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2080,\n        \"wires\": [\n            [\n                \"fb8145a9f8d4f7b2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f9b51424edb0491c\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 1880,\n        \"wires\": [\n            [\n                \"ea87ecfd2af3cc7f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ab34b0a78b2c577\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"100\",\n        \"max\": \"5000\",\n        \"step\": \"100\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1920,\n        \"wires\": [\n            [\n                \"249f44c3a87793ba\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1d4230b3d9b93f63\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 460,\n        \"y\": 1800,\n        \"wires\": [\n            [\n                \"0bb56b1edb12c2cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e3222f0aba88040\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Accramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9d50311679acf215\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_Steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 810,\n        \"y\": 1920,\n        \"wires\": []\n    },\n    {\n        \"id\": \"25d7b4dd2aab8f05\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 1840,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15682cca9622831f\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 1800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8e2d22042bfcb4e8\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 23,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"tt\",\n        \"label\": \"TURNTABLE\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 2040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"56bc3b93af2ebe16\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_acc\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 29,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.1\",\n        \"max\": \"2\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2120,\n        \"wires\": [\n            [\n                \"35422077b53da9bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6ef996f8a36f94c2\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_accramp\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 31,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"500\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2160,\n        \"wires\": [\n            [\n                \"2c000bd53cdb98ca\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0c50fdbb5ac3c373\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_stepsperrotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 25,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 450,\n        \"y\": 2040,\n        \"wires\": [\n            [\n                \"485a4bed5a6bea23\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"213ccfb441a42890\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 30,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ttAccramp\",\n        \"label\": \"Acceleration ramp\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"73c9b4d09dc25e54\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 24,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_steps per Rotation\",\n        \"label\": \"Steps per Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 800,\n        \"y\": 2040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a81824c92f22487d\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 28,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt Acc\",\n        \"label\": \"Acceleration\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9715161858f69649\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 26,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_delay\",\n        \"label\": \"Delay\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2080,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1b3ac50d2c6600c6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 1960,\n        \"wires\": [\n            [\n                \"e0d7c36daa42b3f3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6dcd1f0ccb01a299\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 1960,\n        \"wires\": []\n    },\n    {\n        \"id\": \"16e9a3a71c4bb916\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_angle\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 33,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"180\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2200,\n        \"wires\": [\n            [\n                \"c34111aaec734dd9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"888161059eb9c71c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 32,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_angle\",\n        \"label\": \"Manual angle\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2200,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f4fc72297074c7ae\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"rotor\",\n        \"label\": \"ROTOR\",\n        \"format\": \"\",\n        \"layout\": \"row-center\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 1680,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b1d8f9e21b34102\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tt_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 35,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2240,\n        \"wires\": [\n            [\n                \"89dbbe7d99ddbbaf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2e839fe47a32b5f\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_dir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 22,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"1\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2000,\n        \"wires\": [\n            [\n                \"204b0a5c8629d78a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4519daf0b4b28aef\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 34,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"tt_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5f269ea2c8a53f6c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 21,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_dir\",\n        \"label\": \"Direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b67dfacfc9a23aa5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"advanced settings\",\n        \"links\": [\n            \"62cd775a1c02dac8\"\n        ],\n        \"x\": 95,\n        \"y\": 80,\n        \"wires\": [\n            [\n                \"9b756a1f9b0e7317\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd775a1c02dac8\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"b67dfacfc9a23aa5\"\n        ],\n        \"x\": 955,\n        \"y\": 660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9d94dbc523d989a3\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_delay_after\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"b81e238ccd0a04fe\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0558d6eb9a01862e\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_delay_before\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"a0048747e7300bdc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d47515c9b208bfb7\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_timeout\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.01\",\n        \"max\": \"1\",\n        \"step\": \"0.01\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"9b0d5c521a7822cc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"89c76766c7552b57\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_gain\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 22,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 2540,\n        \"wires\": [\n            [\n                \"9b26ed02296d27c9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c385518eb65a1b27\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_awbg_red\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-10\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2580,\n        \"wires\": [\n            [\n                \"b0ac7e9a7c713b84\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c80833b718d9bf6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_awbg_blue\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-10\",\n        \"max\": \"10\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2620,\n        \"wires\": [\n            [\n                \"827b1a671a77037d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5a3826e112fb24e6\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_contrast\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 24,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2660,\n        \"wires\": [\n            [\n                \"78a1536c167da741\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3182ed7ac02b1509\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_saturation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 26,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"5\",\n        \"step\": \"0.1\",\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 2700,\n        \"wires\": [\n            [\n                \"fe9a5b68fc8c2077\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7fa6337cdf0a0bc8\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_jpeg_q\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"100\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2740,\n        \"wires\": [\n            [\n                \"e27d2613e942f344\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"08275bf96f87b8ef\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"timeout\",\n        \"label\": \"Timeout\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d2d028df4a139f41\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_after\",\n        \"label\": \"Delay after\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c6a65762aa4ffb7b\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"delay_before\",\n        \"label\": \"Delay before\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"780323fd4504b855\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 21,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"gain\",\n        \"label\": \"Gain\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"780bf08b41202135\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"awbg red\",\n        \"label\": \"AWBG red\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"c0faf441fc918538\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"awbg blue\",\n        \"label\": \"AWBG blue\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"93d12b447a39c5bb\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 23,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"contrast\",\n        \"label\": \"Contrast\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e77e6dcd285d3062\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 25,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"saturation\",\n        \"label\": \"Saturation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a7075bc8d5ee1138\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"jpegQ\",\n        \"label\": \"Jpeg Quality\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2740,\n        \"wires\": []\n    },\n    {\n        \"id\": \"282681e7c4351f74\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ext\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 3,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"b17e82651407d8e0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da43c58979737fec\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 2,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ext\",\n        \"label\": \"External Camera\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ef70d61678fe1f11\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"light1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2940,\n        \"wires\": [\n            [\n                \"2c812acffdb330c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fec56a7e913b21d6\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 4,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light1\",\n        \"label\": \"Light 1\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"24929b4629f22070\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"light2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 2980,\n        \"wires\": [\n            [\n                \"ae0654af69446942\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7c6bdc0504aa4cc7\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 6,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"light2\",\n        \"label\": \"Light 2\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 2980,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8c396b060f3d2646\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotordir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3020,\n        \"wires\": [\n            [\n                \"58cf48cfacc979fb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"97568610daccf74a\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 8,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotordir\",\n        \"label\": \"Rotor direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3020,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a3c58ea48c388215\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotorstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 11,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3060,\n        \"wires\": [\n            [\n                \"c7ae206f2fff6810\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6da92aeaeffd95e0\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 10,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotorstep\",\n        \"label\": \"Rotor step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3060,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b5da90eaf6ac562\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotoren\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 13,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3100,\n        \"wires\": [\n            [\n                \"cfebd4a47a68b319\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"12623e4addfa2c22\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 12,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"rotoren\",\n        \"label\": \"Rotor enable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f24cb404d7d09f8a\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ttdir\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 15,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 3140,\n        \"wires\": [\n            [\n                \"90f4d220928e4727\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"542bfb9d92935c2c\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 14,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttdir\",\n        \"label\": \"Turntable direction\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 3140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1f79467df98ce894\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"ttstep\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 17,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 3180,\n        \"wires\": [\n            [\n                \"b05e1e612887f9c2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"170d3b925f7745cc\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 16,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"ttstep\",\n        \"label\": \"Turntable step\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 3180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"65b0130e390c2e67\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"tten\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 19,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 410,\n        \"y\": 3220,\n        \"wires\": [\n            [\n                \"fe22723ce5a3495f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"10ac340984418a58\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 18,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"tten\",\n        \"label\": \"Turntable enable\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 3220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"661614f5bd2c71d6\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"endstop1\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 21,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3260,\n        \"wires\": [\n            [\n                \"2af447a6905b83bc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c18b55859dae5f85\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 20,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop1\",\n        \"label\": \"Endstop 1\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e23a396162026618\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"endstop2\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 23,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"number\",\n        \"delay\": \"0\",\n        \"topic\": \"topic\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"msg\",\n        \"x\": 420,\n        \"y\": 3300,\n        \"wires\": [\n            [\n                \"787a128f84f747c0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"82c1a33014d003e9\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"644b3bcc903d46ca\",\n        \"order\": 22,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"endstop1\",\n        \"label\": \"Endstop 2\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 3300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5255759a7c5b2a74\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"confirm\",\n        \"x\": 680,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"15fd1c9e5610cb85\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8be8015931c663cc\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import save, load_bool\\n\\nif msg['payload'] == True and not load_bool('advanced_settings'):\\n    msg['payload'] = '''<p style=\\\"text-align: center;\\\"><span style=\\\"font-size: 22px;\\\"><strong><u>PLEASE READ :)</u></strong></span></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Modifying the advanced settings can potentially damage your device and/or the connected peripherals.</strong></p>\\n<p style=\\\"margin-left: 20px; text-align: center;\\\"><strong>Please read the given information texts carefully and only change settings, when you are sure about the consequences!</strong></p>\\n'''\\n    return msg\\nelif not msg['payload']: \\n    save('advanced_settings', False)\\n\",\n        \"outputs\": 1,\n        \"x\": 530,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"5255759a7c5b2a74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9d464b2ba1edaf48\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"cam_rotation\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"270\",\n        \"step\": \"90\",\n        \"className\": \"\",\n        \"x\": 420,\n        \"y\": 2780,\n        \"wires\": [\n            [\n                \"b7d3fe0c0b40b3e1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"db98b95693ebce63\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cam_rot\",\n        \"label\": \"Image Rotation\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 760,\n        \"y\": 2780,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6659121906897a1f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1800,\n        \"wires\": [\n            [\n                \"1d4230b3d9b93f63\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0bb56b1edb12c2cf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1800,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"569829eeff715c33\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1920,\n        \"wires\": [\n            [\n                \"1ab34b0a78b2c577\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"249f44c3a87793ba\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1920,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c997e60519341afd\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1960,\n        \"wires\": [\n            [\n                \"1b3ac50d2c6600c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e0d7c36daa42b3f3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"59ecf3a22cd3a669\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2000,\n        \"wires\": [\n            [\n                \"b2e839fe47a32b5f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"204b0a5c8629d78a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"15f02421b30a9ab6\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1840,\n        \"wires\": [\n            [\n                \"8c1a92f2dcc976c7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bb54bbdae6690576\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1840,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"58928befcc61b1f7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1880,\n        \"wires\": [\n            [\n                \"f9b51424edb0491c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ea87ecfd2af3cc7f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1880,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"27bc56f273360ac7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nsteps = parseInt(data);\\nif (steps == 3600){\\n    steps = 3200\\n}\\n\\nmsg.payload = steps\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2040,\n        \"wires\": [\n            [\n                \"0c50fdbb5ac3c373\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f46ced86106306c8\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2160,\n        \"wires\": [\n            [\n                \"6ef996f8a36f94c2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4339704cd8552eb3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2200,\n        \"wires\": [\n            [\n                \"16e9a3a71c4bb916\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ac53bb6150645fe\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2240,\n        \"wires\": [\n            [\n                \"9b1d8f9e21b34102\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b89eb1eaf333c10\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) * 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2080,\n        \"wires\": [\n            [\n                \"2647111c06f2055d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e8927be0e235fa1\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2120,\n        \"wires\": [\n            [\n                \"56bc3b93af2ebe16\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"485a4bed5a6bea23\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_stepsperrotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2040,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"2c000bd53cdb98ca\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_accramp'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2160,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c34111aaec734dd9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_angle'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"89dbbe7d99ddbbaf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nif (msg.payload === 1){\\n    content = '1'\\n}\\nelse{\\n    content = '-1'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2240,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fb8145a9f8d4f7b2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_delay'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload / 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2080,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"35422077b53da9bf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'tt_acc'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d5308090f2b7971a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_timeout'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2420,\n        \"wires\": [\n            [\n                \"d47515c9b208bfb7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b0d5c521a7822cc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_timeout'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2420,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"694d1068bea15171\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2460,\n        \"wires\": [\n            [\n                \"9d94dbc523d989a3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cec3e5e78a40476b\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2500,\n        \"wires\": [\n            [\n                \"0558d6eb9a01862e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b81e238ccd0a04fe\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_after'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a0048747e7300bdc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_delay_before'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6f524f9370a18482\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2540,\n        \"wires\": [\n            [\n                \"89c76766c7552b57\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9b26ed02296d27c9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_gain'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1f87f473e327c3cc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_awbg_red'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2580,\n        \"wires\": [\n            [\n                \"c385518eb65a1b27\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b0ac7e9a7c713b84\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_awbg_red'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cff7ac5f1e061855\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_awbg_blue'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2620,\n        \"wires\": [\n            [\n                \"5c80833b718d9bf6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"827b1a671a77037d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_awbg_blue'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"cf854461c37ca54f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2660,\n        \"wires\": [\n            [\n                \"5a3826e112fb24e6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"78a1536c167da741\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_contrast'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ba10e04dd1761692\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2700,\n        \"wires\": [\n            [\n                \"3182ed7ac02b1509\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fe9a5b68fc8c2077\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_saturation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2700,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a69d216114f908a5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2740,\n        \"wires\": [\n            [\n                \"7fa6337cdf0a0bc8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e27d2613e942f344\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_jpeg_quality'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2740,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f02d4a036a225e87\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2780,\n        \"wires\": [\n            [\n                \"9d464b2ba1edaf48\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b7d3fe0c0b40b3e1\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_rotation'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2780,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"612cccacda1a65aa\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"282681e7c4351f74\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b17e82651407d8e0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_external'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2900,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"3b126549c03a872e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_endstop1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3260,\n        \"wires\": [\n            [\n                \"661614f5bd2c71d6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2af447a6905b83bc\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_endstop1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"954db931f87894ee\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2940,\n        \"wires\": [\n            [\n                \"ef70d61678fe1f11\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2c812acffdb330c5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight1'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2940,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6682c8057e89d087\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2980,\n        \"wires\": [\n            [\n                \"24929b4629f22070\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ae0654af69446942\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_ringlight2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 2980,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"015be401d08047d2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3020,\n        \"wires\": [\n            [\n                \"8c396b060f3d2646\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"58cf48cfacc979fb\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3020,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"1c6c0f8b9ac95659\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3060,\n        \"wires\": [\n            [\n                \"a3c58ea48c388215\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c7ae206f2fff6810\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3060,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"dcee66c0d56c6934\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3100,\n        \"wires\": [\n            [\n                \"9b5da90eaf6ac562\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cfebd4a47a68b319\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_rotor_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ec7d85bb17eb159\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3140,\n        \"wires\": [\n            [\n                \"f24cb404d7d09f8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f42d02a3776a006\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3180,\n        \"wires\": [\n            [\n                \"1f79467df98ce894\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5d70f4715c9a5ae1\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_tt_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3220,\n        \"wires\": [\n            [\n                \"65b0130e390c2e67\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"90f4d220928e4727\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_dir'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"b05e1e612887f9c2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_step'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3180,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fe22723ce5a3495f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_tt_enable'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"58bbe9fc41e0d7b9\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'pin_endstop2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 3300,\n        \"wires\": [\n            [\n                \"e23a396162026618\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"787a128f84f747c0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'pin_endstop2'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 610,\n        \"y\": 3300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"78351089ee9ebeaf\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'ssh'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"40dee936a9abac0d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5fba78ae65eaaf5d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'smb'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"4fd9bb53fdb51a25\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67206663b3881868\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'advanced_settings'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"c833f6243a059d83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3492754252645e62\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'camera'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data.replace(/(\\\\r\\\\n|\\\\n|\\\\r)/gm,\\\"\\\"));\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"a2c1dba3e67be015\",\n                \"6f3d403e157163e4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d16525a31223bc42\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'model'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"80b579a4220e5c23\",\n                \"c6138801b30f091d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f99ec8781a33ec7d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'This is a free piece of software and it is provided as is, without any warranty. <br>There might be functions that need a connection to the internet: '+\\n    '<br><br>By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP  to my servers '+\\n    '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days  The images might be used for further experiments (e.g. machine learning, automation ...). '+\\n    '<br><br>By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\\n    'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\\n    'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\\n    '<br><br>By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\\n    'The raw images and resulting 3d models will never be published without your explicit consent.'+  \\n    '<br><br>If you have any questions you can contact me at info@openscan.eu.'+ \\n    '<br><br>THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\\n    'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \\n    'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \\n    'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"7dc39bd847d16ded\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5f849178998d9082\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"if(msg.payload === 'Agree'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nvar file = 'terms'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nfs.writeFile(filepath+file, String(data), err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"725fd0cab0bddc0e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'hostname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"49259adad52fc214\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"49259adad52fc214\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Hostname\",\n        \"tooltip\": \"\",\n        \"group\": \"0fe66c9190b8a87c\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"Change hostname to:\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 530,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"8001f7c361de7d8c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51521bc6eb44cde5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 250,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb52656f9554dab\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"ssid = msg.payload\\nmsg.topic = 'Add wifi network (' + ssid + ')'\\nmsg.payload = 'Enter Wifi password:'\\nmsg.ssid = ssid\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 650,\n        \"y\": 980,\n        \"wires\": [\n            [\n                \"ebcc98685059b9d4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ebce67b739d1891f\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk/change hostname\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    pass\\n\\nwith open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\n\\nhostname = msg['hostname']\\nif len(hostname) < 4 :\\n    msg['payload'] = ' '\\n    msg['topic'] = 'ERROR - Hostname NOT changed'\\n    return msg\\n    \\n\\nwith open('/etc/hostname', 'w+') as file:\\n    file.write(hostname)\\nos.system('echo ' + hostname + ' | tee /etc/hostname')\\nwith open('/etc/hosts', 'r') as file:\\n    temp = file.read()\\ntemp = temp.replace(old_hostname,hostname)\\nwith open('/etc/hosts', 'w') as file:\\n    file.write(temp)\\nos.system('hostnamectl set-hostname ' + hostname)\\nos.system('systemctl restart avahi-daemon')\\nsave('hostname',hostname)\\nmsg['payload'] = hostname\\nmsg['topic'] = 'Success - Hostname changed'\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 1140,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"667ac2aba819f506\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Confirm\",\n        \"x\": 920,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8001f7c361de7d8c\",\n        \"type\": \"change\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"hostname\",\n                \"pt\": \"msg\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 710,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"667ac2aba819f506\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9bb0adbd716ce347\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"links\": [\n            \"16c76929f88df841\",\n            \"fe3a855fee9e28c6\"\n        ],\n        \"x\": 155,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"d114f4d4d7f31981\",\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f9efcb87b74abbd4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload === 'Cancel'){\\n    return\\n}\\nmsg.forename = msg.payload\\nmsg.topic = 'OpenScanCloud Registration (3/3)'\\nmsg.payload = 'Enter your last name'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 670,\n        \"y\": 1520,\n        \"wires\": [\n            [\n                \"510dbe4d76253bd6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"adc206aa8edd1e41\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSC\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Files&amp;Cloud</h1> <p><strong>Refresh</strong></p> <p style=\\\"margin-left: 20px;\\\">You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.</p> <p style=\\\"margin-left: 20px;\\\">The status (in the table) of the individual sets in the file list will be updated to one of the following:</p> <p style=\\\"margin-left: 20px;\\\"><u>Created&nbsp;</u>- you started the upload of your image set. If you are stuck on this status, please try to restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Initialized&nbsp;</u>- all files have been uploaded and processing will start as soon as possible</p> <p style=\\\"margin-left: 20px;\\\"><u>File approved&nbsp;</u>- the server received and verified your files</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing started</u> - your files are currently being processed</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing failed</u> - there are various reasons why processing might fail. Please check the email for more details or contact me at <a href=\\\"%5C%22mailto:cloud@openscan.eu%5C%22\\\">cloud@openscan.eu</a></p> <p style=\\\"margin-left: 20px;\\\">processing done - check your email, where you should find a link to the 3d model :)</p> <p><strong>Status (on the right column)</strong></p> <p style=\\\"margin-left: 20px;\\\">Indicates, what the device is currently up to.</p> <p style=\\\"margin-left: 20px;\\\"><u>Refreshing&nbsp;</u>- updating all image set&apos;s status</p> <p style=\\\"margin-left: 20px;\\\"><u>Uploading&nbsp;</u>- while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Project started</u> - when the upload of a set was successful</p> <p style=\\\"margin-left: 20px;\\\"><u>Zipping&nbsp;</u>- files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.</p> <p style=\\\"margin-left: 20px;\\\"><u>Combining</u> - two sets into one might take up to a minute. </p> <p><strong>Set</strong></p> <p style=\\\"margin-left: 20px;\\\">select a set from the file list by clicking on a row in the table</p> <p><strong>Download</strong></p> <p style=\\\"margin-left: 20px;\\\">Download the selected set from the OpenScan device to your computer/mobile/tablet</p> <p><strong>Upload</strong></p> <p style=\\\"margin-left: 20px;\\\">Upload the selected file to the OpenScanCloud</p> <p><strong>Combine</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!</p> <p><strong>Delete Set/All</strong></p> <p style=\\\"margin-left: 20px;\\\">Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 590,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"45df91cae421e8e1\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Scan_settings\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Scan Settings</h1> <p><strong>Current Status</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>--READY--&nbsp;</u>- everything is okay and ready to go :)</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-preparing</u> - before starting the routine some time might pass depending on the number of photos</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-stopping</u> - manually ending the routine by pressing the stop button</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-Photo X/Y</u> - Showing the progress of the routine</p> <p style=\\\"margin-left: 20px;\\\"><u>No Camera Found</u> - please check the camera ribbon cable</p> <p style=\\\"margin-left: 20px;\\\"><u>Error: XXX</u> - Please contact <a href=\\\"mailto:info@openscan.eu\\\">info@openscan.eu</a> or post an issue on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a></p> <p><strong>Projectname</strong></p> <p style=\\\"margin-left: 20px;\\\">Each photo set will be saved using the following pattern &nbsp;YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified &apos;default&apos; will be used.</p> <p><strong>Rotor</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the rotor by increments of 5&deg;. Please make sure to start the routine with the camera in the horizontal position.</p> <p><strong>Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the turntable by increments of 15&deg;.</p> <p><strong>Ringlight</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.</p> <p><strong>Photos</strong></p> <p style=\\\"margin-left: 20px;\\\">Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. <u><strong>If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!</strong></u></p> <p><strong>Shutter</strong></p> <p style=\\\"margin-left: 20px;\\\">Again: <strong><u>Less is more</u></strong>! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:</p> <p style=\\\"margin-left: 20px;\\\">- no polarizer: 5-20ms</p> <p style=\\\"margin-left: 20px;\\\">- mostly white object, &nbsp;with polarizer + one ringlight: 50-200ms</p> <p><strong>Crop X/Y</strong></p> <p style=\\\"margin-left: 20px;\\\">Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!</p> <p><strong>Start/Stop</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the buttons to start/stop the routine</p> <p><strong>Reboot/Shutdown</strong></p> <p style=\\\"margin-left: 20px;\\\">In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 760,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e9677b85856b5873\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset rfkill\",\n        \"func\": \"from os import system\\nif \\\"Interface doesn't support scanning\\\" in msg['payload']:\\n    system('rfkill unblock all')\\n    system('ifconfig wlan0 up')\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 390,\n        \"y\": 1100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"91fe20cb16f54293\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1680,\n        \"wires\": [\n            [\n                \"327c8bdde31033a4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"add3e998b097c54f\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"Min Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1680,\n        \"wires\": []\n    },\n    {\n        \"id\": \"da286366433c83a0\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemin'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1680,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"327c8bdde31033a4\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglemin\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1680,\n        \"wires\": [\n            [\n                \"da286366433c83a0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"94288df4c6756197\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 10,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"e531ffe3dcf34eb4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4702a4a09124e27d\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"-90\",\n        \"max\": \"90\",\n        \"step\": \"5\",\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"9ce407cb16f0419a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2cf946c7aab2cbb4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1720,\n        \"wires\": [\n            [\n                \"94288df4c6756197\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e531ffe3dcf34eb4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglemax'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1720,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4da5f650d3845baa\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1760,\n        \"wires\": [\n            [\n                \"4702a4a09124e27d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ce407cb16f0419a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'rotor_anglestart'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fda776c5aa642867\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 9,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglemax\",\n        \"label\": \"Max Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6e9af48a1c4c58c6\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"rotor_anglestart\",\n        \"label\": \"Start Angle\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 780,\n        \"y\": 1760,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9b2bc9849aee310b\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"changeHostname\",\n        \"links\": [\n            \"ec2db55a99bbe3ee\",\n            \"d5175561293ef490\",\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 835,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"8b9e3781511e9231\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8b9e3781511e9231\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk\",\n        \"func\": \"with open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\nif old_hostname == 'raspberrypi':\\n    msg['hostname'] = 'openscan'\\n    msg['payload'] = 'OK'\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3fcbd9fe3acc3fb7\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"scan_arducam\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 1,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Focus Settings</h1> <p><strong>MF - Manual Focus</strong></p> <p style=\\\"margin-left: 20px;\\\">By default, the switch is &apos;off&apos;, which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object). Be aware, that the distance value is only a rough estimate (mm)</p> <p><strong>ST - Stacking</strong></p> <p style=\\\"margin-left: 20px;\\\">Stacking is disabled by default. Once activated, you will be able to set the following:</p> <p style=\\\"margin-left: 20px;\\\"><u>Stacksize</u> - defines the number of photos between the minimal and the maximal focal distance</p> <p style=\\\"margin-left: 20px;\\\"><u>SET</u> press this button to set the maximal/minimal focal distance. Pressing the button a third time will re-set the values.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 760,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d68cccec646e0a0\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable routine\",\n        \"func\": \"msg_enable = {}\\nmsg_disable = {}\\n\\nmsg_enable['enabled'] = True\\nmsg_disable['enabled'] = False\\n\\nif msg['payload'] == 'external':\\n    return msg_enable, msg_disable\\nif msg['payload'] == 'gphoto':\\n    return msg_enable, msg_enable, msg_disable\\n\\nreturn msg_enable\",\n        \"outputs\": 3,\n        \"x\": 560,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"a0ba1aa77c5c8b7c\"\n            ],\n            [\n                \"a42c12e94f65fa01\"\n            ],\n            [\n                \"2d76e5617f13cd6c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a0ba1aa77c5c8b7c\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2aea1727dbea76ce\",\n            \"4f212b44aa487945\",\n            \"65cef204b16f8741\",\n            \"917a194be245384a\"\n        ],\n        \"x\": 675,\n        \"y\": 420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a42c12e94f65fa01\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2aea1727dbea76ce\",\n            \"4f212b44aa487945\",\n            \"65cef204b16f8741\",\n            \"917a194be245384a\"\n        ],\n        \"x\": 715,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2d76e5617f13cd6c\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"65cef204b16f8741\"\n        ],\n        \"x\": 675,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bd80ec228fb9a86d\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 135,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"78351089ee9ebeaf\",\n                \"5fba78ae65eaaf5d\",\n                \"3492754252645e62\",\n                \"d16525a31223bc42\",\n                \"67206663b3881868\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65b38bfeb3fee710\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 155,\n        \"y\": 760,\n        \"wires\": [\n            [\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d3fc91d87d5d5f62\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 135,\n        \"y\": 940,\n        \"wires\": [\n            [\n                \"725fd0cab0bddc0e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cc9c4092edeb43cc\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 135,\n        \"y\": 1020,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\",\n                \"f393400.d87dcc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f0b355967b33dfee\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 175,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"91fe20cb16f54293\",\n                \"2cf946c7aab2cbb4\",\n                \"4da5f650d3845baa\",\n                \"6659121906897a1f\",\n                \"15f02421b30a9ab6\",\n                \"58928befcc61b1f7\",\n                \"569829eeff715c33\",\n                \"c997e60519341afd\",\n                \"59ecf3a22cd3a669\",\n                \"27bc56f273360ac7\",\n                \"9b89eb1eaf333c10\",\n                \"2e8927be0e235fa1\",\n                \"f46ced86106306c8\",\n                \"4339704cd8552eb3\",\n                \"1ac53bb6150645fe\",\n                \"0d48bb415c584420\",\n                \"b6e420121e6466e7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d7c1fb4c028b21a5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 155,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"d5308090f2b7971a\",\n                \"694d1068bea15171\",\n                \"cec3e5e78a40476b\",\n                \"6f524f9370a18482\",\n                \"1f87f473e327c3cc\",\n                \"cff7ac5f1e061855\",\n                \"cf854461c37ca54f\",\n                \"ba10e04dd1761692\",\n                \"a69d216114f908a5\",\n                \"f02d4a036a225e87\",\n                \"1efd4a05aee0b86c\",\n                \"6841e5a392f0fb4f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a67c18aaca2f5fa5\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 155,\n        \"y\": 2900,\n        \"wires\": [\n            [\n                \"612cccacda1a65aa\",\n                \"954db931f87894ee\",\n                \"6682c8057e89d087\",\n                \"015be401d08047d2\",\n                \"1c6c0f8b9ac95659\",\n                \"dcee66c0d56c6934\",\n                \"6ec7d85bb17eb159\",\n                \"4f42d02a3776a006\",\n                \"5d70f4715c9a5ae1\",\n                \"3b126549c03a872e\",\n                \"58bbe9fc41e0d7b9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6d3821bc7f43f8e\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset default\",\n        \"group\": \"4fe6b4c0ade0938a\",\n        \"order\": 14,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Restore default settings\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"This can not be undone!\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Restore default settings?\",\n        \"topicType\": \"str\",\n        \"x\": 930,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"e4be21c38b57f560\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e4be21c38b57f560\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1090,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"9f30de04ced693d3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9f30de04ced693d3\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.overwrite = true\\nif(msg.payload == \\\"Yes\\\"){\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1230,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"80bccc884b0be297\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"80bccc884b0be297\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 1325,\n        \"y\": 300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"34b685aff2080d31\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"boot-cam\",\n        \"func\": \"from OpenScan import load_str\\n\\ncamera_modules = ('imx519', 'imx219', 'ov5647', 'imx477', 'imx378', 'ov9281', 'imx290a', 'imx290b')\\n\\npt1 = \\\"[all]\\\\n\\\\ncamera_auto_detect=0\\\\ngpu_mem=256\\\\ndtoverlay=vc4-fkms-v3d\\\\ndtoverlay=\\\"\\npt3 = \\\",media-controller=1\\\\n\\\"\\n\\nwith open('/boot/config.txt', 'r') as file:\\n    config = file.read()\\n\\ncamera = load_str('camera')\\nif camera not in camera_modules:\\n    msg['payload'] = 'no changes'\\n    return\\n\\nif camera == 'imx290a':\\n    camera = 'imx290,clock-frequency=37125000'\\nelif camera == 'imx290b':\\n    camera = 'imx290,clock-frequency=74250000'\\n\\nconfig_keep = config.split('[all]\\\\n')[0]\\nconfig_new = config_keep + pt1 + camera + pt3\\n\\nwith open('/boot/config.txt', 'w') as file:\\n    file.write(config_new)\\n\\nmsg['topic'] = 'Camera configuration changed'\\nmsg['payload'] = 'Please restart the device'\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 680,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"68cba0c530c6def6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"68cba0c530c6def6\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 830,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f304680180a23479\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Info\",\n        \"x\": 1010,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0d48bb415c584420\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1640,\n        \"wires\": [\n            [\n                \"ce215e159ce7267f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ce215e159ce7267f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Turntable Mode\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 440,\n        \"y\": 1640,\n        \"wires\": [\n            [\n                \"f95f528dec31425c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f95f528dec31425c\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4ebe5baece5ce9f2\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"preview_resolution\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.5\",\n        \"max\": \"10\",\n        \"step\": \"0.5\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"60a415fff23cb55e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ed0498cceceedde\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"preview_res\",\n        \"label\": \"Preview Resolution (Mpx)\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1efd4a05aee0b86c\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_preview_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data)/1000000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2280,\n        \"wires\": [\n            [\n                \"4ebe5baece5ce9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"60a415fff23cb55e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_preview_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload*1000000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6f3d403e157163e4\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Camera\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 5,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Pi Cam v1 - 5mp\",\n                \"value\": \"ov5647\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam v2 - 8mp\",\n                \"value\": \"imx219\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Pi Cam HQ - 12.3mp\",\n                \"value\": \"imx477\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Arducam IMX519 - 16mp\",\n                \"value\": \"imx519\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 a\",\n                \"value\": \"imx290a\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX290 b\",\n                \"value\": \"imx290b\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"IMX378\",\n                \"value\": \"imx378\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OV9281\",\n                \"value\": \"ov9281\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"DSLR (gphoto)\",\n                \"value\": \"gphoto\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"USB Webcam\",\n                \"value\": \"usb_webcam\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"External Camera\",\n                \"value\": \"external\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 400,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"6d68cccec646e0a0\",\n                \"4058a31e942e8f95\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c6138801b30f091d\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"model\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select option\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 3,\n        \"width\": 4,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Please Select\",\n                \"value\": \"None\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Mini\",\n                \"value\": \"OSMini\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"OpenScan Classic\",\n                \"value\": \"OSClassic\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 390,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"896242c5a7e50fa7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4da67c23c7a543a0\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 4,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Camera\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 840,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1fed8676078ea9a7\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"1f7f7e1e24f5ad9b\",\n        \"order\": 2,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Model\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a4b7eea9a9736b0a\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Update&Info\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1>Update&amp;Log</h1> <p><strong>Status</strong></p> <p>See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a> for details and the source code.</p> <p><strong>Updatetype</strong></p> <p>- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras</p> <p>- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well</p> <p>- mini: very simplified firmware for the OpenScanMini + Arducam IMX519</p> <p><strong>Auto-Check update availability</strong></p> <p>Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p> <p>This option is activated by default.</p> <p><strong>Check Updates</strong></p> <p>Alternatively, you can check for updates manually at any time by pressing this button.</p> <p><strong>Download Error Log</strong></p> <p>In case you encounter any errors with your device, please download the error log text and send a copy to <a href=\\\"%5C%22mailto:info@openscan.eu%5C%22\\\">info@openscan.eu</a> or create an issue on <a href=\\\"%5C%22https://github.com/OpenScanEu/OpenScan2%5C%22\\\" rel='\\\\\\\"noopener' target='\\\\\\\"_blank\\\\\\\"'>Github.com</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 750,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b6e420121e6466e7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'routine_secondpass'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"ab8d5cfe9190bb5f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab8d5cfe9190bb5f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Second pass\",\n        \"tooltip\": \"\",\n        \"group\": \"d49a6dfd7fb17096\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 430,\n        \"y\": 1600,\n        \"wires\": [\n            [\n                \"fa51327f0140b045\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fa51327f0140b045\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_secondpass'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 1600,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6841e5a392f0fb4f\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'cam_output_downscale'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"110216d678fad14f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"110216d678fad14f\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Downscale output\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"214d548d564f8ba2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"214d548d564f8ba2\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_output_downscale'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\\n  \\nmsg.enabled = msg.payload\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2320,\n        \"wires\": [\n            [\n                \"1becbff4884b8c1a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8be1ca844a6caa54\",\n        \"type\": \"ui_slider\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"output_resolution\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 8,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0.5\",\n        \"max\": \"20\",\n        \"step\": \"0.5\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 2360,\n        \"wires\": [\n            [\n                \"a6b2c0a0604ccf14\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9ac09d89d791e953\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"93aadb71dee6d977\",\n        \"order\": 7,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"image_res\",\n        \"label\": \"Output Resolution (Mpx)\",\n        \"format\": \"\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 770,\n        \"y\": 2360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1becbff4884b8c1a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_output_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data)/1000000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 2360,\n        \"wires\": [\n            [\n                \"8be1ca844a6caa54\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a6b2c0a0604ccf14\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_output_resolution'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload*1000000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 630,\n        \"y\": 2360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f358de1e64b491bb\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"b30d918661392ab3\",\n            \"44c598049cd533fd\"\n        ],\n        \"x\": 635,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b0629875a30ae1d7\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"get update\",\n        \"func\": \"import json\\nimport requests\\nfrom OpenScan import load_str\\n\\nif not msg['payload']:\\n    msg['status'] = '--READY--'\\n    return msg\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'no internet connection'\\n    return msg\\n\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg['status'] = 'checking updates'\\n\\nreturn msg, msg\",\n        \"outputs\": 2,\n        \"x\": 350,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\"\n            ],\n            [\n                \"fefe45404bdb19c4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c7b6d05a62172432\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Status:\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 170,\n        \"y\": 100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"fefe45404bdb19c4\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"check files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str\\n\\nscope = load_str('update_type')\\nmsg['scope'] = scope\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_old = updatepath + 'update.json'\\n\\ncounter = 0\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    if os.path.isfile(temp):\\n        filesize = os.path.getsize(temp)\\n        msg[scope][i]['filesize2'] = filesize\\n        if filesize == msg[scope][i]['filesize']:\\n            msg[scope][i]['update'] = False\\n            continue\\n    msg[scope][i]['update'] = True\\n\\n    counter += 1\\n\\nif counter == 0:\\n    msg['status'] = 'No new update available'\\nelse:\\n    msg['status'] = 'New update available'\\n    msg['topic'] = msg['status']\\n    msg['payload'] = 'Install & reboot now?'\\n\\nmsg['counter'] = counter\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"1bbe2d769f42c313\",\n                \"ae92a328af306ebb\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d0104e0163745993\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 75,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"38cbf7965d1c1834\",\n                \"49f1ecb29a3f84f4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51cd8c8643e6b46a\",\n        \"type\": \"ui_switch\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"label\": \"Auto-check update availability\",\n        \"tooltip\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 6,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"1ab4c6b4b232a022\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38cbf7965d1c1834\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"51cd8c8643e6b46a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1ab4c6b4b232a022\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 570,\n        \"y\": 140,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ae92a328af306ebb\",\n        \"type\": \"ui_toast\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"NO\",\n        \"cancel\": \"YES\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 670,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"2de63e8e3ae5fb0c\",\n                \"929281fef53e09f8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cbd0afc4aa7b302a\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"update status\",\n        \"links\": [\n            \"1bbe2d769f42c313\",\n            \"42061b28cff81f99\"\n        ],\n        \"x\": 75,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"c7b6d05a62172432\",\n                \"c94623ddd9d95f78\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1bbe2d769f42c313\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 625,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7cf60615d93e696b\",\n        \"type\": \"ui_button\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 7,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Check Updates\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 140,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2de63e8e3ae5fb0c\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"download files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str, save\\n\\n\\nscope = msg['scope']\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\n\\nif msg['payload'] != 'YES':\\n    return\\n\\ncounter = 0\\n\\nfor i in msg[scope]:\\n    if msg[scope][i]['update'] == False:\\n        continue\\n    \\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    \\n    r = requests.get(url + msg[scope][i]['src'])\\n    if r.status_code != 200:\\n        msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\\n        return msg\\n    with open(temp, 'wb+') as file:\\n        file.write(r.content)\\n    shutil.copy(temp, msg[scope][i]['dst'])\\n    \\n    if msg[scope][i]['dst'] == '/boot/config.txt':\\n        save('camera','')\\n    \\n    counter += 1\\n\\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\\n\\nif counter == msg['counter']:\\n    updatepath_temp = updatepath + 'update_temp.json'\\n    updatepath_old = updatepath + 'update.json'\\n    shutil.move(updatepath_temp, updatepath_old)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 840,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\",\n                \"fe3a855fee9e28c6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"929281fef53e09f8\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload == 'YES'){\\n    msg.status = 'Installing updates'\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 810,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"42061b28cff81f99\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"42061b28cff81f99\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"cbd0afc4aa7b302a\"\n        ],\n        \"x\": 955,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"49f1ecb29a3f84f4\",\n        \"type\": \"function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\n\\nmsg.payload = data\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fe3a855fee9e28c6\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9bb0adbd716ce347\"\n        ],\n        \"x\": 955,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5e7d5e4335d37794\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb5fe78e09fec8a\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"msg\",\n        \"func\": \"\\nfrom subprocess import getoutput\\nimport os\\n\\nmsg['os'] = getoutput(\\\"cat /etc/os-release | grep -i 'PRETTY_NAME'\\\")[13:-1]\\nmsg['device'] = getoutput(\\\"cat /proc/device-tree/model\\\")\\nmsg['flask'] = getoutput(\\\"systemctl status flask |grep -i 'Active:'\\\").split(' ')[6]\\nmsg['osdate'] = getoutput(\\\"vcgencmd version\\\").split('\\\\n')[0]\\nmsg['temp'] = getoutput(\\\"vcgencmd measure_temp\\\").split('=')[1]\\ncpu_total = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $2}'\\\")\\ncpu_used = getoutput(\\\"free -m | head -n2 | tail -n1 |awk '{print $3}'\\\")\\nswap_total = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $2}'\\\")\\nswap_used = getoutput(\\\"free -m | head -n3 | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_used = getoutput(\\\"df -h / | tail -n1 |awk '{print $3}'\\\")\\ndiskspace_total = getoutput(\\\"df -h / | tail -n1 |awk '{print $2}'\\\")\\n\\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\\n\\nif msg['flask'] == 'inactive':\\n    os.system('systemctl restart flask')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 170,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"dbc77052ac950624\",\n                \"d97c3068ef5fef96\",\n                \"73a3b828f862312b\",\n                \"901e31453b2bdff8\",\n                \"f983854748ee4763\",\n                \"5347c7c517f5e8c7\",\n                \"3a5016f7003cd72c\",\n                \"6d720c4a4ecd9475\",\n                \"6438b7d060a70d81\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d97c3068ef5fef96\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 2,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"OS:\",\n        \"format\": \"{{msg.os}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"73a3b828f862312b\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 8,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Flask:\",\n        \"format\": \"{{msg.flask}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"dbc77052ac950624\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Device:\",\n        \"format\": \"{{msg.device}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 460,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4c7fa5b5b27b83a5\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"create beta new\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\n\\nscope = 'main'\\n#scope = 'beta'\\n\\nupdatepath = '/home/pi/OpenScan/tmp/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\n\\n## load update.json\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'download update.json failed'\\n    return msg\\n\\nmsg = {}\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg2 = msg.copy()\\ndel msg[scope]\\n\\nmsg[scope]={}\\nmsg[scope]['1'] = {}\\nmsg[scope]['1']['src'] = scope + '/fla.py'\\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\\n\\nmsg[scope]['2'] = {}\\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\\n\\nmsg[scope]['3'] = {}\\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\\n\\nmsg[scope]['4'] = {}\\nmsg[scope]['4']['src'] = scope + '/config.txt'\\nmsg[scope]['4']['dst'] = '/boot/config.txt'\\n\\nmsg[scope]['5'] = {}\\nmsg[scope]['5']['src'] = scope + '/flows.json'\\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\\n\\nmsg[scope]['6'] = {}\\nmsg[scope]['6']['src'] = scope + '/settings.js'\\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\\n\\nmsg[scope]['7'] = {}\\nmsg[scope]['7']['src'] = 'files/logo.jpg'\\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\\n\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    filesize = os.path.getsize(filepath)\\n    msg[scope][i]['filesize'] = filesize\\n\\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\\n    os.system('rm -r /home/pi/OpenScan/tmp/update')    \\nos.makedirs('/home/pi/OpenScan/tmp/update/')\\n\\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\\n    json.dump(msg, f, indent=4)\\n\\nfor i in msg[scope]:\\n    if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\\n        os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\\n    shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\\n\\nmsg['payload'] = 'created with scope: ' + scope\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 300,\n        \"y\": 820,\n        \"wires\": [\n            [\n                \"50f6fb8adf0249d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"80175eb8dc6ad009\",\n        \"type\": \"inject\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 140,\n        \"y\": 820,\n        \"wires\": [\n            [\n                \"4c7fa5b5b27b83a5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3f42560297fe6978\",\n        \"type\": \"ui_template\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"name\": \"Download LOG\",\n        \"order\": 9,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href='tmp/log.txt'>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download error log\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 140,\n        \"y\": 760,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c94623ddd9d95f78\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"get update\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['status'] == \\\"No new update available\\\":\\n    save('updateable',False)\\nelif msg['status'] == \\\"New update available\\\":\\n    save('updateable',True)\\n\",\n        \"outputs\": 1,\n        \"x\": 170,\n        \"y\": 60,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"39a502b38837273d\",\n        \"type\": \"link in\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"links\": [\n            \"1e7457ea9c2c5e09\"\n        ],\n        \"x\": 205,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"b0629875a30ae1d7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"901e31453b2bdff8\",\n        \"type\": \"delay\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"10\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 180,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"2bb5fe78e09fec8a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f983854748ee4763\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 3,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"{{msg.osdate}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 450,\n        \"y\": 480,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5347c7c517f5e8c7\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 4,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU temp:\",\n        \"format\": \"{{msg.temp}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 470,\n        \"y\": 520,\n        \"wires\": []\n    },\n    {\n        \"id\": \"3a5016f7003cd72c\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 5,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"CPU memory:\",\n        \"format\": \"{{msg.cpu}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6d720c4a4ecd9475\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 6,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Swap memory:\",\n        \"format\": \"{{msg.swap}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 480,\n        \"y\": 600,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6438b7d060a70d81\",\n        \"type\": \"ui_text\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"group\": \"3ce32450.e0cffc\",\n        \"order\": 7,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Diskspace:\",\n        \"format\": \"{{msg.diskspace}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 470,\n        \"y\": 640,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d7362e6e0ec7bdaa\",\n        \"type\": \"inject\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"true\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"x\": 130,\n        \"y\": 900,\n        \"wires\": [\n            [\n                \"4ce127c61c3c5966\",\n                \"beacc3dc5398fa79\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4ce127c61c3c5966\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"prepare image creation\",\n        \"func\": \"import os\\n\\n#factory reset, reset wpa, create wpa in boot, rm files\\n#should be done before creating a new raspbian image\\n\\nbasepath = '/home/pi/OpenScan/'\\n\\n#remove files\\n\\ndir = basepath + 'scans/'\\n\\nfor i in ['scans/','tmp/']:\\n    os.system('rm -r ' + basepath + i)\\n    os.mkdir(basepath + i)\\n\\n#delete wifi\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('wpa_cli -i wlan0 reconfigure')\\n\\n#create new wpa_supplicant.conf\\nwith open('/boot/wpa_supplicant.conf','w+') as file:\\n    file.write('country=de\\\\nupdate_config=1\\\\nctrl_interface=/var/run/wpa_supplicant\\\\n\\\\nnetwork={\\\\n scan_ssid=1\\\\n ssid=\\\"wlan name\\\"\\\\n psk=\\\"xxxx\\\"\\\\n}')\\n\\n#rm tmp dir\\n\\n\\n#stop photos:\\nos.system('systemctl stop flask')\\nos.system('rm -r ' + basepath + 'tmp')\\nos.system('mkdir ' + basepath + 'tmp')\\n\\nos.system('systemctl stop nodered')\\n\\n#reset factory\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 330,\n        \"y\": 900,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"beacc3dc5398fa79\",\n        \"type\": \"link out\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 235,\n        \"y\": 940,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8d012912f302be85\",\n        \"type\": \"ui_button\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"group\": \"ddbd496e.93a288\",\n        \"order\": 8,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Show Details/Changelog\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 170,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"5242607a723cc628\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5242607a723cc628\",\n        \"type\": \"python3-function\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"Changelog\",\n        \"func\": \"import requests\\n\\ntempfile = '/home/pi/OpenScan/tmp/changelog'\\n\\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\\nr = requests.get(url, allow_redirects=False)\\n\\nwith open(tempfile,'wb') as file:\\n    file.write(r.content)\\n    \\nwith open(tempfile, 'r') as file:\\n    text = file.read()\\n    \\ntext = text.replace('\\\\n','<br>').replace('*', '&emsp; - ')\\nmsg['payload'] = text\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 390,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"573722197b15bf84\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573722197b15bf84\",\n        \"type\": \"ui_toast\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 570,\n        \"y\": 300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"50f6fb8adf0249d7\",\n        \"type\": \"debug\",\n        \"z\": \"c8e7ecb5849edb9a\",\n        \"name\": \"\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"true\",\n        \"targetType\": \"full\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 570,\n        \"y\": 820,\n        \"wires\": []\n    }\n]"
  },
  {
    "path": "update/main/settings.js",
    "content": "/**\n * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT\n *\n * It can contain any valid JavaScript code that will get run when Node-RED\n * is started.\n *\n * Lines that start with // are commented out.\n * Each entry should be separated from the entries above and below by a comma ','\n *\n * For more information about individual settings, refer to the documentation:\n *    https://nodered.org/docs/user-guide/runtime/configuration\n *\n * The settings are split into the following sections:\n *  - Flow File and User Directory Settings\n *  - Security\n *  - Server Settings\n *  - Runtime Settings\n *  - Editor Settings\n *  - Node Settings\n *\n **/\n\nmodule.exports = {\n\n/*******************************************************************************\n * Flow File and User Directory Settings\n *  - flowFile\n *  - credentialSecret\n *  - flowFilePretty\n *  - userDir\n *  - nodesDir\n ******************************************************************************/\n\n    /** The file containing the flows. If not set, defaults to flows_<hostname>.json **/\n    flowFile: \"flows.json\",\n\n    /** By default, credentials are encrypted in storage using a generated key. To\n     * specify your own secret, set the following property.\n     * If you want to disable encryption of credentials, set this property to false.\n     * Note: once you set this property, do not change it - doing so will prevent\n     * node-red from being able to decrypt your existing credentials and they will be\n     * lost.\n     */\n    credentialSecret: false,\n\n    /** By default, the flow JSON will be formatted over multiple lines making\n     * it easier to compare changes when using version control.\n     * To disable pretty-printing of the JSON set the following property to false.\n     */\n    flowFilePretty: true,\n\n    /** By default, all user data is stored in a directory called `.node-red` under\n     * the user's home directory. To use a different location, the following\n     * property can be used\n     */\n    //userDir: '/home/nol/.node-red/',\n\tuserDir: '/home/pi/OpenScan/settings/.node-red/',\n    /** Node-RED scans the `nodes` directory in the userDir to find local node files.\n     * The following property can be used to specify an additional directory to scan.\n     */\n    //nodesDir: '/home/nol/.node-red/nodes',\n\n/*******************************************************************************\n * Security\n *  - adminAuth\n *  - https\n *  - httpsRefreshInterval\n *  - requireHttps\n *  - httpNodeAuth\n *  - httpStaticAuth\n ******************************************************************************/\n\n    /** To password protect the Node-RED editor and admin API, the following\n     * property can be used. See http://nodered.org/docs/security.html for details.\n     */\n    //adminAuth: {\n    //    type: \"credentials\",\n    //    users: [{\n    //        username: \"admin\",\n    //        password: \"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\",\n    //        permissions: \"*\"\n    //    }]\n    //},\n    \n    /** The following property can be used to enable HTTPS\n     * This property can be either an object, containing both a (private) key\n     * and a (public) certificate, or a function that returns such an object.\n     * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener\n     * for details of its contents.\n     */\n\n    /** Option 1: static object */\n    //https: {\n    //  key: require(\"fs\").readFileSync('privkey.pem'),\n    //  cert: require(\"fs\").readFileSync('cert.pem')\n    //},\n\n    /** Option 2: function that returns the HTTP configuration object */\n    // https: function() {\n    //     // This function should return the options object, or a Promise\n    //     // that resolves to the options object\n    //     return {\n    //         key: require(\"fs\").readFileSync('privkey.pem'),\n    //         cert: require(\"fs\").readFileSync('cert.pem')\n    //     }\n    // },\n\n    /** If the `https` setting is a function, the following setting can be used\n     * to set how often, in hours, the function will be called. That can be used\n     * to refresh any certificates.\n     */\n    //httpsRefreshInterval : 12,\n\n    /** The following property can be used to cause insecure HTTP connections to\n     * be redirected to HTTPS.\n     */\n    //requireHttps: true,\n\n    /** To password protect the node-defined HTTP endpoints (httpNodeRoot),\n     * including node-red-dashboard, or the static content (httpStatic), the\n     * following properties can be used.\n     * The `pass` field is a bcrypt hash of the password.\n     * See http://nodered.org/docs/security.html#generating-the-password-hash\n     */\n    //httpNodeAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n    //httpStaticAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n\n/*******************************************************************************\n * Server Settings\n *  - uiPort\n *  - uiHost\n *  - apiMaxLength\n *  - httpServerOptions\n *  - httpAdminRoot\n *  - httpAdminMiddleware\n *  - httpNodeRoot\n *  - httpNodeCors\n *  - httpNodeMiddleware\n *  - httpStatic\n ******************************************************************************/\n\n    /** the tcp port that the Node-RED web server is listening on */\n//    uiPort: process.env.PORT || 1880,\nuiPort: process.env.PORT || 80,\n    /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.\n     * To listen on all IPv6 addresses, set uiHost to \"::\",\n     * The following property can be used to listen on a specific interface. For\n     * example, the following would only allow connections from the local machine.\n     */\n    //uiHost: \"127.0.0.1\",\n\n    /** The maximum size of HTTP request that will be accepted by the runtime api.\n     * Default: 5mb\n     */\n    //apiMaxLength: '5mb',\n\n    /** The following property can be used to pass custom options to the Express.js\n     * server used by Node-RED. For a full list of available options, refer\n     * to http://expressjs.com/en/api.html#app.settings.table\n     */\n    //httpServerOptions: { },\n\n    /** By default, the Node-RED UI is available at http://localhost:1880/\n     * The following property can be used to specify a different root path.\n     * If set to false, this is disabled.\n     */\n    //httpAdminRoot: '/admin',\nhttpAdminRoot: '/editor',\n    /** The following property can be used to add a custom middleware function\n     * in front of all admin http routes. For example, to set custom http\n     * headers. It can be a single function or an array of middleware functions.\n     */\n    // httpAdminMiddleware: function(req,res,next) {\n    //    // Set the X-Frame-Options header to limit where the editor\n    //    // can be embedded\n    //    //res.set('X-Frame-Options', 'sameorigin');\n    //    next();\n    // },\n\n\n    /** Some nodes, such as HTTP In, can be used to listen for incoming http requests.\n     * By default, these are served relative to '/'. The following property\n     * can be used to specifiy a different root path. If set to false, this is\n     * disabled.\n     */\n    //httpNodeRoot: '/red-nodes',\n\n    /** The following property can be used to configure cross-origin resource sharing\n     * in the HTTP nodes.\n     * See https://github.com/troygoode/node-cors#configuration-options for\n     * details on its contents. The following is a basic permissive set of options:\n     */\n    //httpNodeCors: {\n    //    origin: \"*\",\n    //    methods: \"GET,PUT,POST,DELETE\"\n    //},\n\n    /** If you need to set an http proxy please set an environment variable\n     * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.\n     * For example - http_proxy=http://myproxy.com:8080\n     * (Setting it here will have no effect)\n     * You may also specify no_proxy (or NO_PROXY) to supply a comma separated\n     * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk\n     */\n\n    /** The following property can be used to add a custom middleware function\n     * in front of all http in nodes. This allows custom authentication to be\n     * applied to all http in nodes, or any other sort of common request processing.\n     * It can be a single function or an array of middleware functions.\n     */\n    //httpNodeMiddleware: function(req,res,next) {\n    //    // Handle/reject the request, or pass it on to the http in node by calling next();\n    //    // Optionally skip our rawBodyParser by setting this to true;\n    //    //req.skipRawBodyParser = true;\n    //    next();\n    //},\n\n    /** When httpAdminRoot is used to move the UI to a different root path, the\n     * following property can be used to identify a directory of static content\n     * that should be served at http://localhost:1880/.\n     */\n    //httpStatic: '/home/nol/node-red-static/',\nhttpStatic: '/home/pi/OpenScan/',\n/*******************************************************************************\n * Runtime Settings\n *  - lang\n *  - logging\n *  - contextStorage\n *  - exportGlobalContextKeys\n *  - externalModules\n ******************************************************************************/\n\n     /** Uncomment the following to run node-red in your preferred language.\n      * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko\n      * Some languages are more complete than others.\n      */\n     // lang: \"de\",\n\n     /** Configure the logging output */\n     logging: {\n         /** Only console logging is currently supported */\n         console: {\n             /** Level of logging to be recorded. Options are:\n              * fatal - only those errors which make the application unusable should be recorded\n              * error - record errors which are deemed fatal for a particular request + fatal errors\n              * warn - record problems which are non fatal + errors + fatal errors\n              * info - record information about the general running of the application + warn + error + fatal errors\n              * debug - record information which is more verbose than info + info + warn + error + fatal errors\n              * trace - record very detailed logging + debug + info + warn + error + fatal errors\n              * off - turn off all logging (doesn't affect metrics or audit)\n              */\n             level: \"info\",\n             /** Whether or not to include metric events in the log output */\n             metrics: false,\n             /** Whether or not to include audit events in the log output */\n             audit: false\n         }\n     },\n\n     /** Context Storage\n      * The following property can be used to enable context storage. The configuration\n      * provided here will enable file-based context that flushes to disk every 30 seconds.\n      * Refer to the documentation for further options: https://nodered.org/docs/api/context/\n      */\n     //contextStorage: {\n     //    default: {\n     //        module:\"localfilesystem\"\n     //    },\n     //},\n\n     /** `global.keys()` returns a list of all properties set in global context.\n      * This allows them to be displayed in the Context Sidebar within the editor.\n      * In some circumstances it is not desirable to expose them to the editor. The\n      * following property can be used to hide any property set in `functionGlobalContext`\n      * from being list by `global.keys()`.\n      * By default, the property is set to false to avoid accidental exposure of\n      * their values. Setting this to true will cause the keys to be listed.\n      */\n     exportGlobalContextKeys: false,\n\n     /** Configure how the runtime will handle external npm modules.\n      * This covers:\n      *  - whether the editor will allow new node modules to be installed\n      *  - whether nodes, such as the Function node are allowed to have their\n      * own dynamically configured dependencies.\n      * The allow/denyList options can be used to limit what modules the runtime\n      * will install/load. It can use '*' as a wildcard that matches anything.\n      */\n     externalModules: {\n         // autoInstall: false,   /** Whether the runtime will attempt to automatically install missing modules */\n         // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */\n         // palette: {              /** Configuration for the Palette Manager */\n         //     allowInstall: true, /** Enable the Palette Manager in the editor */\n         //     allowUpload: true,  /** Allow module tgz files to be uploaded and installed */\n         //     allowList: [],\n         //     denyList: []\n         // },\n         // modules: {              /** Configuration for node-specified modules */\n         //     allowInstall: true,\n         //     allowList: [],\n         //     denyList: []\n         // }\n     },\n\n\n/*******************************************************************************\n * Editor Settings\n *  - disableEditor\n *  - editorTheme\n ******************************************************************************/\n\n    /** The following property can be used to disable the editor. The admin API\n     * is not affected by this option. To disable both the editor and the admin\n     * API, use either the httpRoot or httpAdminRoot properties\n     */\n    //disableEditor: false,\n\n    /** Customising the editor\n     * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes\n     * for all available options.\n     */\n    editorTheme: {\n        /** The following property can be used to set a custom theme for the editor.\n         * See https://github.com/node-red-contrib-themes/theme-collection for\n         * a collection of themes to chose from.\n         */\n        //theme: \"\",\n        palette: {\n            /** The following property can be used to order the categories in the editor\n             * palette. If a node's category is not in the list, the category will get\n             * added to the end of the palette.\n             * If not set, the following default order is used:\n             */\n            //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],\n        },\n        projects: {\n            /** To enable the Projects feature, set this value to true */\n            enabled: false,\n            workflow: {\n                /** Set the default projects workflow mode.\n                 *  - manual - you must manually commit changes\n                 *  - auto - changes are automatically committed\n                 * This can be overridden per-user from the 'Git config'\n                 * section of 'User Settings' within the editor\n                 */\n                mode: \"manual\"\n            }\n        },\n        codeEditor: {\n            /** Select the text editor component used by the editor.\n             * Defaults to \"ace\", but can be set to \"ace\" or \"monaco\"\n             */\n            lib: \"ace\",\n            options: {\n                /** The follow options only apply if the editor is set to \"monaco\"\n                 *\n                 * theme - must match the file name of a theme in\n                 * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme\n                 * e.g. \"tomorrow-night\", \"upstream-sunburst\", \"github\", \"my-theme\"\n                 */\n                theme: \"vs\",\n                /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.\n                 * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html\n                 */\n                //fontSize: 14,\n                //fontFamily: \"Cascadia Code, Fira Code, Consolas, 'Courier New', monospace\",\n                //fontLigatures: true,\n            }\n        }\n    },\n\n/*******************************************************************************\n * Node Settings\n *  - fileWorkingDirectory\n *  - functionGlobalContext\n *  - functionExternalModules\n *  - nodeMessageBufferMaxLength\n *  - ui (for use with Node-RED Dashboard)\n *  - debugUseColors\n *  - debugMaxLength\n *  - execMaxBufferSize\n *  - httpRequestTimeout\n *  - mqttReconnectTime\n *  - serialReconnectTime\n *  - socketReconnectTime\n *  - socketTimeout\n *  - tcpMsgQueueSize\n *  - inboundWebSocketTimeout\n *  - tlsConfigDisableLocalFiles\n *  - webSocketNodeVerifyClient\n ******************************************************************************/\n\n    /** The working directory to handle relative file paths from within the File nodes\n     * defaults to the working directory of the Node-RED process.\n     */\n    //fileWorkingDirectory: \"\",\n\n    /** Allow the Function node to load additional npm modules directly */\n    functionExternalModules: true,\n\n    /** The following property can be used to set predefined values in Global Context.\n     * This allows extra node modules to be made available with in Function node.\n     * For example, the following:\n     *    functionGlobalContext: { os:require('os') }\n     * will allow the `os` module to be accessed in a Function node using:\n     *    global.get(\"os\")\n     */\n    functionGlobalContext: {\n        os:require('os'),\n        path:require('path'),\n        fs:require('fs'),\n\n},\n\n    /** The maximum number of messages nodes will buffer internally as part of their\n     * operation. This applies across a range of nodes that operate on message sequences.\n     * defaults to no limit. A value of 0 also means no limit is applied.\n     */\n    //nodeMessageBufferMaxLength: 0,\n\n    /** If you installed the optional node-red-dashboard you can set it's path\n     * relative to httpNodeRoot\n     * Other optional properties include\n     *  readOnly:{boolean},\n     *  middleware:{function or array}, (req,res,next) - http middleware\n     *  ioMiddleware:{function or array}, (socket,next) - socket.io middleware\n     */\n    //ui: { path: \"ui\" },\nui: { path: \"\" },\n    /** Colourise the console output of the debug node */\n    //debugUseColors: true,\n\n    /** The maximum length, in characters, of any message sent to the debug sidebar tab */\n    debugMaxLength: 1000,\n\n    /** Maximum buffer size for the exec node. Defaults to 10Mb */\n    //execMaxBufferSize: 10000000,\n\n    /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */\n    //httpRequestTimeout: 120000,\n\n    /** Retry time in milliseconds for MQTT connections */\n    mqttReconnectTime: 15000,\n\n    /** Retry time in milliseconds for Serial port connections */\n    serialReconnectTime: 15000,\n\n    /** Retry time in milliseconds for TCP socket connections */\n    //socketReconnectTime: 10000,\n\n    /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */\n    //socketTimeout: 120000,\n\n    /** Maximum number of messages to wait in queue while attempting to connect to TCP socket\n     * defaults to 1000\n     */\n    //tcpMsgQueueSize: 2000,\n\n    /** Timeout in milliseconds for inbound WebSocket connections that do not\n     * match any configured node. Defaults to 5000\n     */\n    //inboundWebSocketTimeout: 5000,\n\n    /** To disable the option for using local files for storing keys and\n     * certificates in the TLS configuration node, set this to true.\n     */\n    //tlsConfigDisableLocalFiles: true,\n\n    /** The following property can be used to verify websocket connection attempts.\n     * This allows, for example, the HTTP request headers to be checked to ensure\n     * they include valid authentication information.\n     */\n    //webSocketNodeVerifyClient: function(info) {\n    //    /** 'info' has three properties:\n    //    *   - origin : the value in the Origin header\n    //    *   - req : the HTTP request\n    //    *   - secure : true if req.connection.authorized or req.connection.encrypted is set\n    //    *\n    //    * The function should return true if the connection should be accepted, false otherwise.\n    //    *\n    //    * Alternatively, if this function is defined to accept a second argument, callback,\n    //    * it can be used to verify the client asynchronously.\n    //    * The callback takes three arguments:\n    //    *   - result : boolean, whether to accept the connection or not\n    //    *   - code : if result is false, the HTTP error status to return\n    //    *   - reason: if result is false, the HTTP reason string to return\n    //    */\n    //},\n}\n"
  },
  {
    "path": "update/mini/Arducam.py",
    "content": "import time\nimport os\n\ntry:\n    import v4l2\nexcept Exception as e:\n    print(e)\n    print(\"Try to install v4l2-fix\")\n    try:\n        from pip import main as pipmain\n    except ImportError:\n        from pip._internal import main as pipmain\n    pipmain(['install', 'v4l2-fix'])\n    print(\"\\nTry to run the focus program again.\")\n    exit(0)\n\nimport fcntl\nimport errno\n\n# # Type\n# v4l2.V4L2_CTRL_TYPE_INTEGER\n# v4l2.V4L2_CTRL_TYPE_BOOLEAN\n# v4l2.V4L2_CTRL_TYPE_MENU\n# v4l2.V4L2_CTRL_TYPE_BUTTON\n# v4l2.V4L2_CTRL_TYPE_INTEGER64\n# v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n# # Flags\n# v4l2.V4L2_CTRL_FLAG_DISABLED\n# v4l2.V4L2_CTRL_FLAG_GRABBED\n# v4l2.V4L2_CTRL_FLAG_READ_ONLY\n# v4l2.V4L2_CTRL_FLAG_UPDATE\n# v4l2.V4L2_CTRL_FLAG_INACTIVE\n# v4l2.V4L2_CTRL_FLAG_SLIDER\n\ndef assert_valid_queryctrl(queryctrl):\n    return queryctrl.type & (\n        v4l2.V4L2_CTRL_TYPE_INTEGER\n        | v4l2.V4L2_CTRL_TYPE_BOOLEAN\n        | v4l2.V4L2_CTRL_TYPE_MENU\n        | v4l2.V4L2_CTRL_TYPE_BUTTON\n        | v4l2.V4L2_CTRL_TYPE_INTEGER64\n        | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS\n        | 7\n        | 8\n        | 9\n    ) and queryctrl.flags & (\n        v4l2.V4L2_CTRL_FLAG_DISABLED\n        | v4l2.V4L2_CTRL_FLAG_GRABBED\n        | v4l2.V4L2_CTRL_FLAG_READ_ONLY\n        | v4l2.V4L2_CTRL_FLAG_UPDATE\n        | v4l2.V4L2_CTRL_FLAG_INACTIVE\n        | v4l2.V4L2_CTRL_FLAG_SLIDER\n    )\n\ndef get_device_controls_menu(fd, queryctrl):\n    querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum)\n    while querymenu.index <= queryctrl.maximum:\n        fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu)\n        yield querymenu\n        querymenu.index += 1\n\ndef get_device_controls_by_class(fd, control_class):\n    # enumeration by control class\n    queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n    while True:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n        except IOError as e:\n            assert e.errno == errno.EINVAL\n            break\n        if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class:\n            break\n        yield queryctrl\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL)\n\ndef getdict(struct):\n    val = dict((field, getattr(struct, field)) for field, _ in struct._fields_)\n    val.pop(\"reserved\")\n    return val\n\ndef get_device_controls(fd):\n    # original enumeration method\n    queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE)\n    while queryctrl.id < v4l2.V4L2_CID_LASTP1:\n        try:\n            fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl)\n            print(queryctrl.name)\n        except IOError as e:\n            # this predefined control is not supported by this device\n            assert e.errno == errno.EINVAL\n            queryctrl.id += 1\n            continue\n        queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1)\n\ndef get_ctrls(vd):\n    ctrls = []\n    # enumeration by control class\n    for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA):\n        for queryctrl in get_device_controls_by_class(vd, class_):\n            ctrl = getdict(queryctrl)\n            if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    # print(querymenu.name)\n                    ctrl[\"menu\"].append(querymenu.name)\n\n            if queryctrl.type == 9:\n                ctrl[\"menu\"] = []\n                for querymenu in get_device_controls_menu(vd, queryctrl):\n                    ctrl[\"menu\"].append(querymenu.index)\n            ctrls.append(ctrl)\n    return ctrls\n\ndef set_ctrl(vd, id, value):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    ctrl.value = value\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n\ndef get_ctrl(vd, id):\n    ctrl = v4l2.v4l2_control()\n    ctrl.id = id\n    try:\n        fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl)\n    except IOError as e:\n        print(e)\n        return None\n    return ctrl.value\n\n\nclass Focuser:\n    FOCUS_ID = 0x009a090a\n    dev = None\n\n    def __init__(self, dev=0):\n        self.focus_value = 0\n        self.dev = dev\n\n        if type(dev) == int or (type(dev) == str and dev.isnumeric()):\n            self.dev = \"/dev/video{}\".format(dev)\n\n        self.fd = open(self.dev, 'r')\n        self.ctrls = get_ctrls(self.fd)\n        self.hasFocus = False\n        for ctrl in self.ctrls:\n            if ctrl['id'] == Focuser.FOCUS_ID:\n                self.hasFocus = True\n                self.opts[Focuser.OPT_FOCUS][\"MIN_VALUE\"] = ctrl['minimum']\n                self.opts[Focuser.OPT_FOCUS][\"MAX_VALUE\"] = ctrl['maximum']\n                self.opts[Focuser.OPT_FOCUS][\"DEF_VALUE\"] = ctrl['default']\n                self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID)\n\n        if not self.hasFocus:\n            raise RuntimeError(\"Device {} has no focus_absolute control.\".format(self.dev))\n\n    def read(self):\n        return self.focus_value\n\n    def write(self, value):\n        self.focus_value = value\n        # os.system(\"v4l2-ctl -d {} -c focus_absolute={}\".format(self.dev, value))\n        set_ctrl(self.fd, Focuser.FOCUS_ID, value)\n\n    OPT_BASE    = 0x1000\n    OPT_FOCUS   = OPT_BASE | 0x01\n    OPT_ZOOM    = OPT_BASE | 0x02\n    OPT_MOTOR_X = OPT_BASE | 0x03\n    OPT_MOTOR_Y = OPT_BASE | 0x04\n    OPT_IRCUT   = OPT_BASE | 0x05\n    opts = {\n        OPT_FOCUS : {\n            \"MIN_VALUE\": 0,\n            \"MAX_VALUE\": 1000,\n            \"DEF_VALUE\": 0,\n        },\n    }\n    def reset(self,opt,flag = 1):\n        info = self.opts[opt]\n        if info == None or info[\"DEF_VALUE\"] == None:\n            return\n        self.set(opt,info[\"DEF_VALUE\"])\n\n    def get(self,opt,flag = 0):\n        info = self.opts[opt]\n        return self.read()\n\n    def set(self,opt,value,flag = 1):\n        info = self.opts[opt]\n        if value > info[\"MAX_VALUE\"]:\n            value = info[\"MAX_VALUE\"]\n        elif value < info[\"MIN_VALUE\"]:\n            value = info[\"MIN_VALUE\"]\n        self.write(value)\n        print(\"write: {}\".format(value))\n\n    def __del__(self):\n        self.fd.close()\n\npass\n"
  },
  {
    "path": "update/mini/OpenScan.py",
    "content": "basepath = '/home/pi/OpenScan/'\nfrom os.path import isfile\n\ndef load_bool(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    if value == '1' or value == 'True' or value =='true':\n        value = True\n    else:\n        value = False\n    return value\n\ndef load_str(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = file.read().replace('\\n','')\n    return value\n\ndef load_int(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = int(file.read().replace('\\n',''))\n    return value\n\ndef load_float(name):\n    filename = basepath+'settings/'+name\n    if not isfile(filename):\n        return\n    with open(filename, 'r') as file:\n        value = float(file.read().replace('\\n',''))\n    return value\n\ndef save(name, value):\n    filename = basepath+'settings/'+name\n    with open(filename, 'w+') as file:\n        file.write(str(value))\n    return\n\ndef OpenScanCloud(cmd, msg):\n    from requests import get\n    osc_user = 'openscan'\n    osc_pw = 'free'\n    osc_server = 'http://openscanfeedback.dnsuser.de:1334/'\n\n    try:\n        r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg)\n    except:\n        r = type('obj', (object,), {'status_code' : 404, 'text':None})\n    return r\n\ndef camera(cmd, msg = {}):\n    from requests import get\n    flask = 'http://127.0.0.1:1312/'\n    r = get(flask + cmd, params=msg)\n    return r.status_code\n\ndef motorrun(motor,angle):\n    import RPi.GPIO as GPIO\n    from time import sleep\n    from math import cos\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n\n    spr = load_int(motor + '_stepsperrotation')\n    dirpin = load_int('pin_' + motor + '_dir')\n    steppin = load_int('pin_' + motor +'_step')\n    dir = load_int(motor + '_dir')\n    ramp = load_int(motor + '_accramp')\n    acc = load_float(motor + '_acc')\n    delay_init = load_float(motor + '_delay')\n    delay = delay_init\n\n    step_count=int(angle*spr/360) * dir\n    GPIO.setup(dirpin, GPIO.OUT)\n    GPIO.setup(steppin, GPIO.OUT)\n    if (step_count>0):\n        GPIO.output(dirpin, GPIO.HIGH)\n    if(step_count<0):\n        GPIO.output(dirpin, GPIO.LOW)\n        step_count=-step_count\n    for x in range(step_count):\n        GPIO.output(steppin, GPIO.HIGH)\n        if x<=ramp and x<=step_count/2:\n            delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc)\n            #delay=delay_init+(ramp-x)*(delay_init)/acc\n        elif step_count-x<=ramp and x>step_count/2:\n            delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc)\n            #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc\n        else:\n            delay = delay_init\n        sleep(delay)\n        GPIO.output(steppin, GPIO.LOW)\n        sleep(delay)\n\ndef ringlight(number,state):\n    import RPi.GPIO as GPIO\n    pin = load_int('pin_ringlight' + str(number))\n    GPIO.setwarnings(False)\n    GPIO.setmode(GPIO.BCM)\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, state)\n\ndef take_photo(file):\n    from os import system\n    filepath = basepath + file\n\n    model=load_str('model')\n\n\n\n    shutter = str(load_int('cam_shutter'))\n    saturation = load_str('cam_saturation')\n    contrast = load_str('cam_contrast')\n    awbg_red = load_str('cam_awbg_red')\n    awbg_blue = load_str('cam_awbg_blue')\n    gain = load_str('cam_gain')\n    quality = load_int('cam_jpeg_quality')\n    filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg'\n    #width = load_str('cam_resx')\n    #height = load_str('cam_resy')\n    timeout = load_str('cam_timeout')\n    cropx = load_int('cam_cropx')/200\n    cropy = load_int('cam_cropy')/200\n    rotation = load_int('cam_rotation')\n    AF = load_bool('cam_AFmode')\n    camera = load_str('camera')\n\n\n    if camera == 'imx519' and AF == True:\n        autofocus = ' --autofocus '\n    else:\n        autofocus = ''\n\n    cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1'\n#    cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout  +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + \",\" + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus\n    system(cmd)\n    return cmd\n\ndef get_points(samples=1):\n    from math import pi, sqrt, acos, atan2, cos, sin\n\n    points = []\n    phi = pi * (3. - sqrt(5.))\n    for i in range(int(samples)):\n        y = 1 - (i / float(samples - 1)) * 2\n        radius = sqrt(1 - y * y)\n        theta = phi * i\n        x = cos(theta) * radius\n        z = sin(theta) * radius\n        r=sqrt(x*x+y*y+z*z)\n        theta_neu=acos(z/r)*180/pi\n        phi_neu=atan2(y,x)*180/pi\n        points.append((theta_neu-90,phi_neu))\n    points.sort()\n    return points\n\ndef create_coordinates(angle_min, angle_max,point_count):\n    point_count_final=point_count\n    if angle_max < angle_min:\n        a = angle_min\n        angle_min = angle_max\n        angle_max = a\n    point_count=point_count*90/(angle_max-angle_min)\n    actual_points=0\n    while actual_points<point_count_final:\n        points=get_points(point_count)\n        filtered=[]\n        for x,y in points:\n            if x>angle_min and x<angle_max and len(filtered)<point_count_final:\n                filtered.append((x,y))\n        actual_points=len(filtered)\n\n        if point_count-actual_points>20:\n            point_count=point_count+3\n        else:\n            point_count=point_count+1\n    return filtered\n\n"
  },
  {
    "path": "update/mini/config.txt",
    "content": "# For more options and information see\n# http://rpf.io/configtxt\n# Some settings may impact device functionality. See link above for details\n\n\n# uncomment if you get no picture on HDMI for a default \"safe\" mode\n#hdmi_safe=1\nhdmi_blanking=2\n\n# uncomment the following to adjust overscan. Use positive numbers if console\n# goes off screen, and negative if there is too much border\n#overscan_left=16\n#overscan_right=16\n#overscan_top=16\n#overscan_bottom=16\n\n# uncomment to force a console size. By default it will be display's size minus\n# overscan.\n#framebuffer_width=1280\n#framebuffer_height=720\n\n# uncomment if hdmi display is not detected and composite is being output\n#hdmi_force_hotplug=1\n\n# uncomment to force a specific HDMI mode (this will force VGA)\n#hdmi_group=1\n#hdmi_mode=1\n\n# uncomment to force a HDMI mode rather than DVI. This can make audio work in\n# DMT (computer monitor) modes\n#hdmi_drive=2\n\n# uncomment to increase signal to HDMI, if you have interference, blanking, or\n# no display\n#config_hdmi_boost=4\n\n# uncomment for composite PAL\n#sdtv_mode=2\n\n#uncomment to overclock the arm. 700 MHz is the default.\n#arm_freq=800\n\n# Uncomment some or all of these to enable the optional hardware interfaces\n#dtparam=i2c_arm=on\n#dtparam=i2s=on\n#dtparam=spi=on\n\n# Uncomment this to enable infrared communication.\n#dtoverlay=gpio-ir,gpio_pin=17\n#dtoverlay=gpio-ir-tx,gpio_pin=18\n\n# Additional overlays and parameters are documented /boot/overlays/README\n\n# Enable audio (loads snd_bcm2835)\ndtparam=audio=on\n\n# Automatically load overlays for detected cameras\ncamera_auto_detect=0\n\n# Automatically load overlays for detected DSI displays\ndisplay_auto_detect=1\n\n# Enable DRM VC4 V3D driver\n#dtoverlay=vc4-kms-v3d\nmax_framebuffers=2\n\n# Disable compensation for displays with overscan\ndisable_overscan=1\n\n[cm4]\n# Enable host mode on the 2711 built-in XHCI USB controller.\n# This line should be removed if the legacy DWC2 controller is required\n# (e.g. for USB device mode) or if USB support is not required.\notg_mode=1\n\n[pi4]\n# Run as fast as firmware / board allows\narm_boost=1\n\n[all]\n\ncamera_auto_detect=0\ngpu_mem=256\ndtoverlay=vc4-fkms-v3d\ndtoverlay=imx519,media-controller=0\ndtoverlay=imx519\n"
  },
  {
    "path": "update/mini/fla.py",
    "content": "from flask import Flask, make_response, jsonify, request, abort\nfrom PIL import Image\nimport gphoto2 as gp\nfrom time import sleep\nimport shutil\nfrom OpenScan import load_int, load_float, load_bool\nimport RPi.GPIO as GPIO\n\nGPIO.setwarnings(False)\nGPIO.setmode(GPIO.BCM)\n\napp = Flask(__name__)\n\nbasedir = '/home/pi/OpenScan/'\n\n###################################################################################################################\n@app.route('/gphoto_init', methods=['get'])\ndef gphoto_init():\n    global camera\n    camera = gp.Camera()\n    camera.init()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_preview', methods=['get'])\ndef gphoto_preview():\n    filepath = str(request.args.get('filepath'))\n    camera_file = gp.gp_camera_capture_preview(camera)[1]\n    target = basedir + filepath\n    camera_file.save(target)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_capture', methods=['get'])\ndef gphoto_capture():\n    filepath = str(request.args.get('filepath'))\n    file_path = camera.capture(gp.GP_CAPTURE_IMAGE)\n    camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL)\n    camera_file.save(basedir + filepath)\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_test', methods=['get'])\ndef gphoto_test():\n    text = camera.get_summary()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/gphoto_exit', methods=['get'])\ndef gphoto_exit():\n    global camera\n    camera.exit()\n    return ({}, 200)\n###################################################################################################################\n@app.route('/crop', methods=['get'])\ndef crop():\n    downscale_threshold = 1000\n    filepath_in = basedir + str(request.args.get('filepath_in'))\n    filepath_out = basedir + str(request.args.get('filepath_out'))\n    cropx = int(request.args.get('cropx'))/200\n    cropy = int(request.args.get('cropy'))/200\n    rotation = int(request.args.get('rotation'))\n    preview = str(request.args.get('preview'))\n \n    with Image.open(filepath_in) as img:\n        w,h = img.size\n        if cropx != 0 or cropy != 0:\n            img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy)))\n        if rotation == 90:\n            img  = img.transpose(Image.ROTATE_90)\n        elif rotation == 180:\n            img= img.transpose(Image.ROTATE_180)\n        elif rotation == 270:\n            img= img.transpose(Image.ROTATE_270)\n        if preview == \"True\":\n            w,h = img.size\n            if w > downscale_threshold or h > downscale_threshold:\n                downscale = max(w/downscale_threshold,h/downscale_threshold)\n            img = img.resize((int(w/downscale),int(h/downscale)),Image.ANTIALIAS)\n        img.save(filepath_out, quality=95, subsampling=0)\n    return ({}, 200)\n\n###################################################################################################################\n@app.route('/external_capture', methods=['get'])\ndef external_capture():\n    pin = load_int('pin_external')\n    delay_before = load_float('cam_delay_before')\n    timeout = load_float('cam_timeout')/1000\n    delay_after = load_float('cam_delay_after')\n    GPIO.setup(pin, GPIO.OUT)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_before)\n    GPIO.output(pin, GPIO.HIGH)\n    sleep(timeout)\n    GPIO.output(pin, GPIO.LOW)\n    sleep(delay_after)\n    return ({}, 200)\n\n\n\n\nif __name__ == '__main__':\n    app.run(host='127.0.0.1', port=1312, debug=False, threaded=True)\n#    app.run(host='0.0.0.0', port=1312, debug=False, threaded=True)\n"
  },
  {
    "path": "update/mini/flows.json",
    "content": "[\n    {\n        \"id\": \"829d803b6033a693\",\n        \"type\": \"tab\",\n        \"label\": \"HOME\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"1613373abaf77a2c\",\n        \"type\": \"tab\",\n        \"label\": \"SCAN\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"4981d84ef1a366d1\",\n        \"type\": \"tab\",\n        \"label\": \"Files&Cloud\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"017bd4e4a428bee5\",\n        \"type\": \"tab\",\n        \"label\": \"SETTINGS\",\n        \"disabled\": false,\n        \"info\": \"\",\n        \"env\": []\n    },\n    {\n        \"id\": \"b3150b13e34b1fe8\",\n        \"type\": \"ui_tab\",\n        \"name\": \"OpenScan\",\n        \"icon\": \"dashboard\",\n        \"order\": 1,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"b6e9c2df6b28ff66\",\n        \"type\": \"ui_base\",\n        \"theme\": {\n            \"name\": \"theme-dark\",\n            \"lightTheme\": {\n                \"default\": \"#0094CE\",\n                \"baseColor\": \"#0094CE\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"darkTheme\": {\n                \"default\": \"#097479\",\n                \"baseColor\": \"#097479\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\",\n                \"edited\": true,\n                \"reset\": false\n            },\n            \"customTheme\": {\n                \"name\": \"Untitled Theme 1\",\n                \"default\": \"#4B7930\",\n                \"baseColor\": \"#4B7930\",\n                \"baseFont\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n            },\n            \"themeState\": {\n                \"base-color\": {\n                    \"default\": \"#097479\",\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-titlebar-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"page-backgroundColor\": {\n                    \"value\": \"#111111\",\n                    \"edited\": false\n                },\n                \"page-sidebar-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"group-textColor\": {\n                    \"value\": \"#0eb8c0\",\n                    \"edited\": false\n                },\n                \"group-borderColor\": {\n                    \"value\": \"#555555\",\n                    \"edited\": false\n                },\n                \"group-backgroundColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"widget-textColor\": {\n                    \"value\": \"#eeeeee\",\n                    \"edited\": false\n                },\n                \"widget-backgroundColor\": {\n                    \"value\": \"#097479\",\n                    \"edited\": false\n                },\n                \"widget-borderColor\": {\n                    \"value\": \"#333333\",\n                    \"edited\": false\n                },\n                \"base-font\": {\n                    \"value\": \"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif\"\n                }\n            },\n            \"angularTheme\": {\n                \"primary\": \"indigo\",\n                \"accents\": \"blue\",\n                \"warn\": \"red\",\n                \"background\": \"grey\",\n                \"palette\": \"light\"\n            }\n        },\n        \"site\": {\n            \"name\": \"OpenScan 3D Scanner\",\n            \"hideToolbar\": \"false\",\n            \"allowSwipe\": \"false\",\n            \"lockMenu\": \"false\",\n            \"allowTempTheme\": \"true\",\n            \"dateFormat\": \"DD/MM/YYYY\",\n            \"sizes\": {\n                \"sx\": 46,\n                \"sy\": 46,\n                \"gx\": 10,\n                \"gy\": 10,\n                \"cx\": 6,\n                \"cy\": 6,\n                \"px\": 6,\n                \"py\": 6\n            }\n        }\n    },\n    {\n        \"id\": \"729f9ea6e3513c9b\",\n        \"type\": \"ui_group\",\n        \"name\": \"Home\",\n        \"tab\": \"b3150b13e34b1fe8\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"65ae49b64fa0d83e\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Settings\",\n        \"icon\": \"dashboard\",\n        \"order\": 4,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"e23b837a9f040895\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Scan\",\n        \"icon\": \"dashboard\",\n        \"order\": 2,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"7aaf184330605300\",\n        \"type\": \"ui_group\",\n        \"name\": \"Settings\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"90223f7ddc082321\",\n        \"type\": \"ui_group\",\n        \"name\": \"Arducam\",\n        \"tab\": \"e23b837a9f040895\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": 10,\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"7625f9c9e8dbc5c6\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3b4bd36726be16d5\",\n        \"type\": \"ui_group\",\n        \"name\": \"Settings\",\n        \"tab\": \"65ae49b64fa0d83e\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"b5fdd57b.15eda8\",\n        \"type\": \"ui_group\",\n        \"name\": \"Main\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 1,\n        \"disp\": false,\n        \"width\": 13,\n        \"collapse\": false\n    },\n    {\n        \"id\": \"db43d646.2074c8\",\n        \"type\": \"ui_group\",\n        \"name\": \"OpenScanCloud\",\n        \"tab\": \"15a222ed.d70a7d\",\n        \"order\": 2,\n        \"disp\": false,\n        \"width\": \"6\",\n        \"collapse\": false,\n        \"className\": \"\"\n    },\n    {\n        \"id\": \"15a222ed.d70a7d\",\n        \"type\": \"ui_tab\",\n        \"name\": \"Files&Cloud\",\n        \"icon\": \"dashboard\",\n        \"order\": 3,\n        \"disabled\": false,\n        \"hidden\": false\n    },\n    {\n        \"id\": \"d9b28c5fbfd37509\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 21,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"cc7d61f1cfd44578\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 24,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"bba223eda03b67eb\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 25,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"e28012f571abd8dc\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 28,\n        \"width\": 1,\n        \"height\": 1\n    },\n    {\n        \"id\": \"b32d71fea63612b7\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 1,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"4cf8566a9121fe3f\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"spacer\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1\n    },\n    {\n        \"id\": \"2cc829a5.610f36\",\n        \"type\": \"ui_group\",\n        \"name\": \"group-test\",\n        \"tab\": \"\",\n        \"order\": 3,\n        \"disp\": false,\n        \"width\": 3,\n        \"collapse\": false\n    },\n    {\n        \"id\": \"65285822f24120f4\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 8,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"90d444dfdfccf138\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 15,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"c9dbf6219837d6d7\",\n        \"type\": \"ui_spacer\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"spacer\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 22,\n        \"width\": 6,\n        \"height\": 1\n    },\n    {\n        \"id\": \"3fe52603e2ac73b6\",\n        \"type\": \"ui_template\",\n        \"z\": \"829d803b6033a693\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"name\": \"Background\",\n        \"order\": 1,\n        \"width\": 0,\n        \"height\": 0,\n        \"format\": \"<style>\\n    body {\\n        background-image: url(\\\"/files/logo.jpg\\\");\\n        background-repeat: no-repeat;\\n        background-attachment: fixed;\\n        background-position: center;\\n\\n    }\\n    #OpenScan_Home {\\n        background-color: transparent !important;\\n        border-color: transparent !important;\\n    }\\n\\n</style>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"global\",\n        \"className\": \"\",\n        \"x\": 110,\n        \"y\": 40,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4468f691.103eb8\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 2,\n        \"width\": 2,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"SCAN\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"1\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6560dd25.9e76c4\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 4,\n        \"width\": 2,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Settings\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"3\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"62cd5288.2805fc\",\n        \"type\": \"ui_ui_control\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"events\": \"all\",\n        \"x\": 300,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"71e72293.91c6fc\",\n        \"type\": \"ui_button\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"group\": \"729f9ea6e3513c9b\",\n        \"order\": 3,\n        \"width\": 2,\n        \"height\": 2,\n        \"passthru\": false,\n        \"label\": \"Files\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"2\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"62cd5288.2805fc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"88edad7ca53698fd\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"x\": 90,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"000a811a215e08d4\",\n                \"83c2b5ea51f0fec3\",\n                \"88fde4ab78c965d7\",\n                \"bee62d2a99cbc63b\",\n                \"4fa53fb9738cb1d3\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bd75f33b8a57c522\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"92c98e6ce7cd25f9\"\n        ],\n        \"x\": 335,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"000a811a215e08d4\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"enable\",\n        \"func\": \"msg.enabled = true\\nmsg.payload = 1\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 360,\n        \"wires\": [\n            [\n                \"bd75f33b8a57c522\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"83c2b5ea51f0fec3\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 240,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"6b94bf2295b1b31d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6b94bf2295b1b31d\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"disable\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"a1d29e56599da0bd\"\n        ],\n        \"x\": 335,\n        \"y\": 400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"88fde4ab78c965d7\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_cloud'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = 'ready'\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 440,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"960912e90ba5b5bc\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"started1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"397ab7f44b893c89\",\n            \"65145c939b6647e2\",\n            \"65b38bfeb3fee710\",\n            \"6d1e12f51f9af0b6\",\n            \"788fabff98c7973c\",\n            \"9b2bc9849aee310b\",\n            \"a1e14624058e74cd\",\n            \"a67c18aaca2f5fa5\",\n            \"bd80ec228fb9a86d\",\n            \"cc9c4092edeb43cc\",\n            \"d3fc91d87d5d5f62\",\n            \"d7c1fb4c028b21a5\",\n            \"e5f38b4a07a5e278\",\n            \"f0b355967b33dfee\",\n            \"d0104e0163745993\",\n            \"5e7d5e4335d37794\",\n            \"e0965e490d53617f\",\n            \"612a7556ab11cf7d\",\n            \"482bc06e02eec5b9\"\n        ],\n        \"x\": 615,\n        \"y\": 720,\n        \"wires\": []\n    },\n    {\n        \"id\": \"168d72a54504b327\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"5/0.1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.1\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"5\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"x\": 100,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"6c6ef2255a7d39e5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6c6ef2255a7d39e5\",\n        \"type\": \"link out\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"repeat 5s/0.1s\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"61990987acd0f263\",\n            \"2415272f42ce468c\"\n        ],\n        \"x\": 195,\n        \"y\": 640,\n        \"wires\": []\n    },\n    {\n        \"id\": \"bee62d2a99cbc63b\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"global\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nglobal.set('combine', false)\\nglobal.set('focus', 2838)\\nglobal.set('focus1', 0)\\nglobal.set('focus2', 0)\\n\\nglobal.set('focuser', true)\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 230,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"544d20f02215011a\",\n        \"type\": \"function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"CREATE FACTORY DEFAULT\",\n        \"func\": \"msg = { \\n'overwrite':msg.overwrite,\\n'settings':\\n    {\\n    'advanced_settings':false,\\n    'cameras':{\\n        'imx519':[4656,3496],\\n        'imx219':[3280,2464],\\n        'imx477':[4056,3040],\\n        'ov5647':[2592,1944],\\n        'imx378':[3840,2880],\\n        'ov9271':[1280,800],\\n        'imx290a':[1920,1080],\\n        'imx290b':[1920,1080],\\n    },\\n    'cam_AFmode':true,\\n    'cam_STmode':true,\\n    'cam_stacksize':2,\\n    'cam_awbg_blue':0,\\n    'cam_awbg_red':0,\\n    'cam_contrast':1,\\n    'cam_cropx':0,\\n    'cam_cropy':0,\\n    'cam_delay_after':0,\\n    'cam_delay_before':0,\\n    'camera':'',\\n    'cam_gain':1,\\n    'cam_jpeg_quality':95,\\n    'cam_rotation':0,\\n    'cam_saturation':1,\\n    'cam_shutter':50000,\\n    'cam_timeout':800,\\n    'hostname':'',\\n    'model':'',\\n    'osc_credit':'',\\n    'osc_limit_filesize':'',\\n    'osc_limit_photos':'',\\n    'osc_splitsize':200000000,\\n    'pin_endstop1':24,\\n    'pin_endstop2':25,\\n    'pin_external':10,\\n    'pin_ringlight1':17,\\n    'pin_ringlight2':27,\\n    'pin_rotor_dir':5,\\n    'pin_rotor_enable':23,\\n    'pin_rotor_step':6,\\n    'pin_tt_dir':9,\\n    'pin_tt_enable':22,\\n    'pin_tt_step':11,\\n    'rotor_acc':1,\\n    'rotor_accramp':2000,\\n    'rotor_angle':10,\\n    'rotor_anglemax':60,\\n    'rotor_anglemin':-40,\\n    'rotor_anglestart':0,\\n    'rotor_delay':0.0001,\\n    'rotor_dir':1,\\n    'rotor_stepsperrotation':48000,\\n    'routine_photocount':50,\\n    'routine_projectname':'default',\\n    'smb':true,\\n    'ssh':true,\\n    'status_cloud':'ready',\\n    'status_internal_cam':'--READY--',\\n    'status_uploadprogress':'',\\n    'terms':false,\\n    'token':'',\\n    'tt_acc':1,\\n    'tt_accramp':200,\\n    'tt_angle':10,\\n    'tt_delay':0.0001,\\n    'tt_dir':1,\\n    'tt_stepsperrotation':3200,\\n    'cam_focus':2838,\\n    'cam_focus1':0,\\n    'cam_focus2':0,\\n    'uploadprogress':'',\\n    'update_type':'main',\\n    'update_auto':true,\\n    'turntable_mode':true,\\n}}\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 310,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"c77552216a8bb781\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1f0ed7d5a9d670e\",\n        \"type\": \"inject\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"overwrite\",\n                \"v\": \"false\",\n                \"vt\": \"bool\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"0.1\",\n        \"topic\": \"\",\n        \"x\": 90,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c77552216a8bb781\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"chk files\",\n        \"func\": \"from os import listdir\\n\\nbasedir = '/home/pi/OpenScan/settings/'\\n\\nmsg['payload'] = ''\\n\\nfiles = listdir(basedir)\\n\\nfor i in msg['settings']:\\n    if msg['overwrite'] != True:\\n        if i not in files:\\n            with open(basedir + i, 'w+') as file:\\n                file.write(str(msg['settings'][i]))        \\n    else:\\n        with open(basedir + i, 'w+') as file:\\n            file.write(str(msg['settings'][i])) \\n\\nmsg['payload'] = True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 520,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"960912e90ba5b5bc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"38783aea9cc317a6\",\n        \"type\": \"link in\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"factory reset\",\n        \"links\": [\n            \"80bccc884b0be297\"\n        ],\n        \"x\": 135,\n        \"y\": 760,\n        \"wires\": [\n            [\n                \"544d20f02215011a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4fa53fb9738cb1d3\",\n        \"type\": \"python3-function\",\n        \"z\": \"829d803b6033a693\",\n        \"name\": \"create log\",\n        \"func\": \"import subprocess\\n\\n\\nlog = '############################################DMESG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"dmesg\\\")\\nlog += '\\\\n############################################SYSLOG############################################\\\\n'\\nlog += subprocess.getoutput(\\\"tail -10000 /var/log/syslog\\\")\\n\\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\\n    file.write(log)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 240,\n        \"y\": 480,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"828e5298.d2192\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇐\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"b12e54fb.3141b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"96c7e241.458e6\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"⇒\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"37f52dd4.bd7572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2e854876.6b6008\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇑\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"555aea34.b3b5e4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"753817f.1b9b3e8\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": true,\n        \"label\": \"⇓\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"num\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"9905e0c9.dddcd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8775044.3aa3ef8\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Turntable\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 100,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9e8a2d23.bf6ce\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Rotor\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 90,\n        \"y\": 240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"555aea34.b3b5e4\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 280,\n        \"wires\": [\n            [\n                \"46e00b45.c24ca4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9905e0c9.dddcd\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 320,\n        \"wires\": [\n            [\n                \"6ee089cb343a35ef\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b12e54fb.3141b8\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 400,\n        \"wires\": [\n            [\n                \"c1871a2b9af5419a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"37f52dd4.bd7572\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"lmt 0.2/s\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"0.1\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"0.2\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": true,\n        \"outputs\": 1,\n        \"x\": 220,\n        \"y\": 440,\n        \"wires\": [\n            [\n                \"42b9f1fc49e69f54\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"46e00b45.c24ca4\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 280,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6ee089cb343a35ef\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Rotor right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('rotor',-load_int('rotor_angle'))\",\n        \"outputs\": 1,\n        \"x\": 370,\n        \"y\": 320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"42b9f1fc49e69f54\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT right\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',-load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 360,\n        \"y\": 440,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c1871a2b9af5419a\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"TT left\",\n        \"func\": \"from OpenScan import motorrun, load_int\\n\\nmotorrun('tt',load_int('tt_angle'))\",\n        \"outputs\": 1,\n        \"x\": 350,\n        \"y\": 400,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"aebad788761dce4a\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"routine_photocount\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 14,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"10\",\n        \"max\": \"300\",\n        \"step\": \"10\",\n        \"className\": \"\",\n        \"x\": 350,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"ce28a0b5bfb0d5a1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"107a030938cbfea9\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"aebad788761dce4a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ce28a0b5bfb0d5a1\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_photocount'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 540,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"84d6b96c8ebaac96\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadF\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseFloat(data) / 1000;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"470b10726d298834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"470b10726d298834\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"shutter \",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 16,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"1\",\n        \"max\": \"300\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"44c3947a9b92d32d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"44c3947a9b92d32d\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_shutter'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload * 1000)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 580,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"069bcf58b1fe44cd\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 13,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"photocount\",\n        \"label\": \"Photos\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8dc7df1de59cb03a\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 15,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"shutter\",\n        \"label\": \"Shutter (ms)\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 650,\n        \"y\": 580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"cc69dba8d54a29dd\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Crop X\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 18,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"99\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"c2b2ab5524271123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e3a90602605fb9e9\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Crop Y\",\n        \"label\": \" \",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 20,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"end\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": \"0\",\n        \"max\": \"99\",\n        \"step\": \"1\",\n        \"className\": \"\",\n        \"x\": 310,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"26f17a7f406df73c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9c6b48b7b4cc4e1a\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"cc69dba8d54a29dd\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c470fd0b15356206\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = parseInt(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"e3a90602605fb9e9\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c2b2ab5524271123\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropx'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"26f17a7f406df73c\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'cam_cropy'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fecf5cff888bb570\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 17,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cropx\",\n        \"label\": \"Crop X (%)\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 650,\n        \"y\": 620,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0ee4950bd21498bd\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 19,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"cropy\",\n        \"label\": \"Crop Y (%)\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 650,\n        \"y\": 660,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ebbf11b55d758806\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 4,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 320,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"67385b196c517ac6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4b3112a9ec6c487\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload=\\\"default\\\"\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"ebbf11b55d758806\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"67385b196c517ac6\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'routine_projectname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\nconst content = String(msg.payload).replace(/ /g, '_')\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 510,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"4dd7285c2b0fd79b\",\n        \"type\": \"ui_slider\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"ringlight\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": true,\n        \"outs\": \"all\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"min\": 0,\n        \"max\": \"3\",\n        \"step\": 1,\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"873dace18a23fdf2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"873dace18a23fdf2\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"LED\",\n        \"func\": \"from OpenScan import ringlight\\nval = msg['payload']\\n\\nif val == 0:\\n    ringlight(1,False)\\n    ringlight(2,False)\\nelif val == 1:\\n    ringlight(1,True)\\n    ringlight(2,False)\\nelif val == 2:\\n    ringlight(1,False)\\n    ringlight(2,True)\\nelif val == 3:\\n    ringlight(1,True)\\n    ringlight(2,True)\\n\",\n        \"outputs\": 1,\n        \"x\": 510,\n        \"y\": 700,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"9e30e33a1520fee0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadI\",\n        \"func\": \"msg.payload = 0\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 190,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"4dd7285c2b0fd79b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dd287f40385922f\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start \",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 22,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-play\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"enabled\",\n        \"topicType\": \"str\",\n        \"x\": 150,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"33d94a04b96a2de0\",\n                \"6d15f717d5a11002\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"579f2211199fd6ab\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 23,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-stop\",\n        \"payload\": \"numberofphotos\",\n        \"payloadType\": \"global\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 750,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"1787f08ed7070ddd\",\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"431f917c2541ae48\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Routine\",\n        \"func\": \"from OpenScan import load_str, load_int, motorrun, create_coordinates, take_photo, save, load_bool, camera\\nfrom time import sleep, strftime\\nfrom zipfile import ZipFile, ZIP_DEFLATED\\nfrom os import system\\nfrom os.path import isfile\\nfrom Arducam import Focuser\\n\\nstatus = load_str(\\\"status_internal_cam\\\")\\n\\nif status == \\\"no camera found\\\":\\n    return\\n\\nif not status == \\\"Routine-stopping\\\":\\n    save('status_internal_cam','Routine-preparing')\\n\\nprojectname=load_str(\\\"routine_projectname\\\")\\nphotocount = load_int('routine_photocount') #vorher point_count\\nangle_max = load_int('rotor_anglemax')\\nangle_min = load_int('rotor_anglemin')\\nangle_start = load_int('rotor_anglestart')\\n\\ncounter = 0\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\n\\nif not 'projectcode' in msg:\\n    projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\\n    msg['projectcode'] = projectcode\\n    msg['counter'] = -1\\n    if isfile(zippath):\\n        system('rm ' + zippath)\\n    sleep(1)\\n\\nprojectcode = msg['projectcode']\\nmsg['counter'] += 1\\n\\ntt_mode = load_bool('turntable_mode')\\n\\nif tt_mode == False:\\n    coordinates = create_coordinates(angle_min,angle_max,photocount)\\nelse:\\n    angle_start = 0\\n    coordinates = []\\n    for i in range (photocount):\\n        coordinates.append([0,360/photocount*(i+1)])\\n\\nposition_last = (angle_start , 0)\\n\\nzip = ZipFile(zippath, \\\"a\\\",ZIP_DEFLATED, allowZip64=True)\\n\\nfor position in coordinates:\\n    counter += 1\\n    if load_str('status_internal_cam') == \\\"Routine-stopping\\\":\\n        break\\n\\n\\n    rotor_angle = position_last[0] - position[0]\\n    if abs(rotor_angle) > 180:\\n        rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\\n\\n    tt_angle = position_last[1] - position[1]\\n    #if abs(tt_angle) > 180:\\n    #    tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\\n        \\n    motorrun('rotor', rotor_angle)\\n    motorrun('tt', tt_angle)\\n\\n\\n\\n    msg['cropx'] = load_int('cam_cropx')\\n    msg['cropy'] = load_int('cam_cropy')\\n    msg['rotation'] = load_int('cam_rotation')\\n    msg['filepath_in'] = 'tmp/tmp.jpg'\\n    msg['filepath_out'] = 'tmp/tmp.jpg'\\n    msg['filepath'] = 'tmp/tmp.jpg'\\n    if load_str('status_internal_cam') != \\\"Routine-stopping\\\":\\n        save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount))\\n    take_photo('tmp/tmp.jpg')\\n    camera('/crop',msg)\\n\\n\\n        \\n    zip.write(temppath, projectname + '_' + str(counter + msg['counter']*photocount) + \\\".jpg\\\")\\n    system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\\n\\n    position_last = position\\n\\n\\nzip.close()\\n\\n\\nsave('status_internal_cam','Routine-done')\\n\\nmotorrun('rotor',position_last[0] - angle_start)\\nmotorrun('tt',position_last[1])\\n\\nsave('status_internal_cam','--READY--')\\n\\nmsg['topic'] = 'Scan done'\\nmsg['payload'] = 'Do you want to continue scanning or finish this project?'\\nmsg['enabled'] = False\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"x\": 300,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"db7eea74d3bf892b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1787f08ed7070ddd\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"stop\",\n        \"func\": \"from OpenScan import load_str, save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\\n    return\\n\\nsave('status_internal_cam', 'Routine-stopping')\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 920,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b13dfd9f8d3711\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\"\n        ],\n        \"x\": 395,\n        \"y\": 800,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5ba05110851a5096\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"reboot\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 26,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"reboot\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-repeat\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 90,\n        \"y\": 1000,\n        \"wires\": [\n            [\n                \"16c76929f88df841\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"152d402caa595189\",\n        \"type\": \"ui_button\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"shutdown\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 27,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"shutdown\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"fa-power-off\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 100,\n        \"y\": 1040,\n        \"wires\": [\n            [\n                \"597bfb653e8cddbf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"16c76929f88df841\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9bb0adbd716ce347\"\n        ],\n        \"x\": 215,\n        \"y\": 1000,\n        \"wires\": []\n    },\n    {\n        \"id\": \"597bfb653e8cddbf\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"fc9abb94c35eec56\"\n        ],\n        \"x\": 215,\n        \"y\": 1040,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9654deebb668e012\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"1s\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"1\",\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 110,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"c1c044f3c2139f68\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8367cfa0bf5bc5df\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine\",\n        \"links\": [\n            \"210ef5246d1a8790\",\n            \"84608db962fd9932\",\n            \"8689e938.dd9e38\",\n            \"f20f2dbc.0f123\",\n            \"e9b13dfd9f8d3711\",\n            \"96bdb9417e38810f\",\n            \"fb13752beddee9f2\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 55,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"7dd287f40385922f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fb13752beddee9f2\",\n        \"type\": \"link out\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"482bc06e02eec5b9\"\n        ],\n        \"x\": 865,\n        \"y\": 880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"95439678bb2df2a2\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"msg.flag = global.get('flag')\\nif (global.get('flag_pw')== true){\\n    return msg\\n}\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1160,\n        \"wires\": [\n            [\n                \"04cc2467807d2d6b\",\n                \"14f9617b5b301318\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"948a3ae4444685f2\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 650,\n        \"y\": 1200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"04cc2467807d2d6b\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"flag_pw false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag_pw\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 430,\n        \"y\": 1200,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"12f1399b240830bf\",\n        \"type\": \"exec\",\n        \"z\": \"1613373abaf77a2c\",\n        \"command\": \" v4l2-ctl --list-formats-ext\",\n        \"addpay\": \"\",\n        \"append\": \"\",\n        \"useSpawn\": \"true\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"check cam\",\n        \"x\": 190,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"6222f781629c72e7\"\n            ],\n            [\n                \"6222f781629c72e7\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"6222f781629c72e7\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\n\\ncontent = '--READY--'\\n\\nif (msg.payload.includes('Cannot open device')){\\n    content = 'no camera found'\\n}\\n\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 350,\n        \"y\": 100,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e978bf8c53d1f15a\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Settings internal cam\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 40,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e9566588c5e40637\",\n        \"type\": \"inject\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"4s/0.5\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"0.5\",\n        \"crontab\": \"\",\n        \"once\": true,\n        \"onceDelay\": \"10\",\n        \"topic\": \"Repeat\",\n        \"payload\": \"0.2\",\n        \"payloadType\": \"str\",\n        \"x\": 120,\n        \"y\": 1160,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"14f9617b5b301318\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"Take Preview Shot\",\n        \"func\": \"from time import time, sleep\\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\\n\\nstatus = load_str('status_internal_cam')\\n\\nif msg['flag'] == False and not 'Routine' in status:\\n    return\\n\\nmsg['payload']=\\\"/tmp/preview.jpg?ts=\\\"+str(int(time()*10))\\n\\nif status!=\\\"--READY--\\\" and status!=\\\"ERROR:flask\\\":\\n    return msg\\n\\nmsg['cropx'] = load_int('cam_cropx')\\nmsg['cropy'] = load_int('cam_cropy')\\nmsg['rotation'] = load_int('cam_rotation')\\nmsg['filepath_in'] = 'tmp/tmp.jpg'\\nmsg['filepath_out'] = 'tmp/preview.jpg'\\nmsg['preview'] = True\\n\\ntake_photo('tmp/tmp.jpg')\\n\\ntry:\\n    camera('/crop',msg)\\nexcept:\\n    save('status_internal_cam','ERROR: flask')\\n    pass\\n    \\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 450,\n        \"y\": 1160,\n        \"wires\": [\n            [\n                \"948a3ae4444685f2\",\n                \"8f5d87ce24c40b11\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1118d0965ff7c40b\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 3,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"projectname\",\n        \"label\": \"Projectname\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 500,\n        \"wires\": []\n    },\n    {\n        \"id\": \"82c8ad50ecfbc755\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"name\": \"ringlight\",\n        \"label\": \"Ringlight\",\n        \"format\": \"\",\n        \"layout\": \"row-left\",\n        \"className\": \"\",\n        \"x\": 660,\n        \"y\": 700,\n        \"wires\": []\n    },\n    {\n        \"id\": \"33d94a04b96a2de0\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"global.set('flag', false)\\n\\nvar file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\n\\n\\nif (data === 'no camera found'){\\n    return\\n}\\n\\nmsg.enabled = true\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 290,\n        \"y\": 920,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c1c044f3c2139f68\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"579f2211199fd6ab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9a368472a72fbc48\",\n        \"type\": \"comment\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview arducam with focus\",\n        \"info\": \"\",\n        \"x\": 160,\n        \"y\": 1100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8f5d87ce24c40b11\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"preview_arducam\",\n        \"order\": 8,\n        \"width\": 10,\n        \"height\": 12,\n        \"format\": \"<div align=\\\"center\\\" >\\n<img ng-src= {{msg.payload}} style=\\\"width: auto; height: auto;max-width: 502px;max-height: 606px\\\"/>\\n</div>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 670,\n        \"y\": 1160,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"282efe64332193c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focus\",\n        \"func\": \"from OpenScan import load_str\\n\\nif load_str('camera') != 'imx519':\\n    return\\n\\nfrom Arducam import Focuser\\n\\n\\nif msg['focuser'] == True:\\n    focuser = Focuser('/dev/v4l-subdev1')\\n    focuser.write(msg['focus'])\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 1110,\n        \"y\": 1320,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"64b16ef47ab6d859\",\n        \"type\": \"ui_switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"MF\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 3,\n        \"width\": 1,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"style\": \"\",\n        \"onvalue\": \"false\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"true\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"f017f67a8d4a3750\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f017f67a8d4a3750\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable\",\n        \"func\": \"let fs = global.get('fs');\\nfilepath = '/home/pi/OpenScan/settings/';\\n\\nvar file = 'status_internal_cam'\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data != '--READY--'){\\n    return\\n}\\n\\nfile = 'cam_AFmode'\\ncontent = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n});\\n\\nglobal.set('AF',msg.payload)\\nmsg.enabled = false\\nif (msg.payload == false){\\n    msg.enabled = true\\n}\\nif (msg.payload == true){\\n    file = 'cam_focus1'\\n    content = String(0)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    file = 'cam_focus2'\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    \\n    file = 'cam_stacksize'\\n    content = String(2)\\n    fs.writeFile(filepath + file, content, err => {\\n      if (err) {\\n        return\\n      }\\n    });\\n    global.set('focus1', 0)\\n    global.set('focus2', 0)\\n\\n}\\n\\n\\nmsg.focus = global.get('focus')\\nmsg.payload = 'down'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"5c39bd09.702d84\",\n                \"74521cf72050b515\",\n                \"b70e8c24ee011258\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65145c939b6647e2\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"64b16ef47ab6d859\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5ea18678.975138\",\n        \"type\": \"trigger\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"20ms\",\n        \"op1\": \"\",\n        \"op2\": \"0\",\n        \"op1type\": \"pay\",\n        \"op2type\": \"str\",\n        \"duration\": \"-20\",\n        \"extend\": false,\n        \"overrideDelay\": false,\n        \"units\": \"ms\",\n        \"reset\": \"\",\n        \"bytopic\": \"all\",\n        \"topic\": \"topic\",\n        \"outputs\": 1,\n        \"x\": 730,\n        \"y\": 1300,\n        \"wires\": [\n            [\n                \"fd93843e238cc9ce\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5c39bd09.702d84\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F+\",\n        \"order\": 4,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: -1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-plus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1260,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dcfb5cce.0431a\",\n        \"type\": \"switch\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"property\": \"payload\",\n        \"propertyType\": \"msg\",\n        \"rules\": [\n            {\n                \"t\": \"eq\",\n                \"v\": \"1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"-1\",\n                \"vt\": \"num\"\n            },\n            {\n                \"t\": \"eq\",\n                \"v\": \"up\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"checkall\": \"true\",\n        \"repair\": false,\n        \"outputs\": 3,\n        \"x\": 550,\n        \"y\": 1280,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"5ea18678.975138\",\n                \"f4a41b1e7b221486\"\n            ],\n            [\n                \"8cdd0a6b.40bcd8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8cdd0a6b.40bcd8\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"reset\",\n                \"pt\": \"msg\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 560,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"5ea18678.975138\",\n                \"e9b3837b1ffb0360\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"74521cf72050b515\",\n        \"type\": \"ui_template\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"name\": \"F-\",\n        \"order\": 5,\n        \"width\": 1,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"padding:0 0, margin:0 ; width:100%; height:100%;\\\" ng-mousedown=\\\"send({payload: 1})\\\" ng-mouseup=\\\"send({payload: 'up'})\\\"><i class=\\\"fa fa-minus\\\"></i> </md-button>\",\n        \"storeOutMessages\": true,\n        \"fwdInMessages\": true,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 410,\n        \"y\": 1300,\n        \"wires\": [\n            [\n                \"dcfb5cce.0431a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7219f62c9fdc6753\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 6,\n        \"width\": 2,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"{{msg.payload}}\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 1130,\n        \"y\": 1280,\n        \"wires\": []\n    },\n    {\n        \"id\": \"b70e8c24ee011258\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"global\",\n        \"func\": \"if (msg.payload == 'down'){\\n    msg.enabled = false\\n    msg.payload = ' '\\n    msg.focuser = global.get('focuser')\\n    return msg\\n}\\n\\n\\nmsg.enabled = true\\n\\nsign = msg.payload\\nfocus = global.get('focus')\\nif (focus > 3000){\\n    focusstep = 5\\n}\\nelse if (focus <=3000 && focus > 2000){\\n    focusstep = 3\\n}\\nelse{\\n    focusstep = 2\\n}\\n\\n\\nfocus = focus + sign * focusstep\\n\\nsign = msg.payload\\nif (focus > 4000){\\n    distance = 6\\n    focus = 4000\\n}\\nelse if (focus > 1200 && focus <= 4000){\\n    distance = 737086 * Math.pow(focus, -1.4096)\\n}\\nelse if (focus <= 1200){\\n    distance = 999\\n    if (focus <=0){\\n       focus = 0\\n    }\\n}\\n\\n\\nglobal.set('focus', focus)\\nmsg.focus = focus\\nmsg.distance = distance\\ndistance = distance * 10\\nmsg.focuser = global.get('focuser')\\nmsg.payload = String(distance.toFixed(1)) + 'mm'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 970,\n        \"y\": 1300,\n        \"wires\": [\n            [\n                \"7219f62c9fdc6753\",\n                \"282efe64332193c8\",\n                \"704a9f89089d1f25\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4a41b1e7b221486\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser f\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e9b3837b1ffb0360\",\n        \"type\": \"change\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"focuser t\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"focuser\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 740,\n        \"y\": 1340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"fd93843e238cc9ce\",\n        \"type\": \"delay\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"10ms\",\n        \"pauseType\": \"delay\",\n        \"timeout\": \"20\",\n        \"timeoutUnits\": \"milliseconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 850,\n        \"y\": 1300,\n        \"wires\": [\n            [\n                \"b70e8c24ee011258\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dfbfe28bac5c4221\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"90223f7ddc082321\",\n        \"order\": 2,\n        \"width\": 1,\n        \"height\": 1,\n        \"name\": \"MF\",\n        \"label\": \"MF\",\n        \"format\": \"\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 150,\n        \"y\": 1300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"704a9f89089d1f25\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"save\",\n        \"func\": \"var file = 'cam_focus'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.focus)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 1110,\n        \"y\": 1360,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"917a194be245384a\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65cef204b16f8741\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable shutter\",\n        \"links\": [\n            \"2d76e5617f13cd6c\",\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"84d6b96c8ebaac96\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2aea1727dbea76ce\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropx\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"9c6b48b7b4cc4e1a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f212b44aa487945\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"enable cropy\",\n        \"links\": [\n            \"a0ba1aa77c5c8b7c\",\n            \"a42c12e94f65fa01\"\n        ],\n        \"x\": 55,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"c470fd0b15356206\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d1e12f51f9af0b6\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start camchk\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 100,\n        \"wires\": [\n            [\n                \"12f1399b240830bf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8ebd1dcb5db156ed\",\n        \"type\": \"ui_text\",\n        \"z\": \"1613373abaf77a2c\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 2,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"Current Status:\",\n        \"format\": \"<font color= {{msg.color}} > {{msg.payload}} </font>\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 160,\n        \"wires\": []\n    },\n    {\n        \"id\": \"94a7aec739f9266b\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'status_internal_cam'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\n\\nif (data === 'no camera found'){\\n    msg.color = 'red'\\n}\\n\\nreturn msg\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"8ebd1dcb5db156ed\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2415272f42ce468c\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start status\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 55,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"94a7aec739f9266b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a1e14624058e74cd\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"start routine settings\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 55,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"f4b3112a9ec6c487\",\n                \"107a030938cbfea9\",\n                \"84d6b96c8ebaac96\",\n                \"9c6b48b7b4cc4e1a\",\n                \"c470fd0b15356206\",\n                \"9e30e33a1520fee0\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1daf9e3a5bd5ab48\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"msg\",\n        \"func\": \"global.set('flag_pw', true)\\nglobal.set('flag', true)\\nmsg.enabled = true\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"fb13752beddee9f2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d15f717d5a11002\",\n        \"type\": \"function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"disable\",\n        \"func\": \"msg.enabled = false\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 300,\n        \"y\": 800,\n        \"wires\": [\n            [\n                \"e9b13dfd9f8d3711\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"db7eea74d3bf892b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"1613373abaf77a2c\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Finish\",\n        \"cancel\": \"Continue\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 450,\n        \"y\": 840,\n        \"wires\": [\n            [\n                \"0b8661103366f834\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"0b8661103366f834\",\n        \"type\": \"python3-function\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"continue\",\n        \"func\": \"from os import system\\nfrom os.path import isfile\\n\\nif msg['payload'] == 'Continue':\\n    msg['enabled'] = True\\n    return msg,None\\n\\nbasepath = '/home/pi/OpenScan/'\\ntemppath = basepath + 'tmp/tmp.jpg'\\nzippath = basepath + 'tmp/tmp.zip'\\nprojectcode = msg['projectcode']\\n\\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\\n\\nif isfile(zippath):\\n    system('rm ' + zippath)\\n\\nreturn None, msg\",\n        \"outputs\": 2,\n        \"x\": 600,\n        \"y\": 880,\n        \"wires\": [\n            [\n                \"431f917c2541ae48\",\n                \"579f2211199fd6ab\"\n            ],\n            [\n                \"1daf9e3a5bd5ab48\",\n                \"579f2211199fd6ab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"482bc06e02eec5b9\",\n        \"type\": \"link in\",\n        \"z\": \"1613373abaf77a2c\",\n        \"name\": \"preview\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 55,\n        \"y\": 1200,\n        \"wires\": [\n            [\n                \"95439678bb2df2a2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ea54fcc2.cfcc2\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"get dirs\",\n        \"func\": \"from glob import glob\\nimport os\\nfrom zipfile import ZipFile\\n\\ndef set_stats(stat):\\n    try:\\n        with open(directory+set[:-4]+\\\"/\\\"+stat,\\\"r\\\") as file:\\n            stat=file.read()\\n    except:\\n        stat=\\\"\\\"\\n    return stat\\n\\ntable=[]\\ndirectory=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nfor d in glob(directory+\\\"*.zip\\\"):\\n    set=os.path.basename(d)\\n\\n    try:\\n        with ZipFile(d, 'r') as f:\\n            photos = len(f.namelist())\\n\\n        size = float(int(float(os.path.getsize(d))/100000))/10\\n        size_full= os.path.getsize(d)\\n        status=set_stats(\\\"status\\\")\\n        expiration=set_stats(\\\"expiration\\\")\\n        download=set_stats(\\\"download\\\")\\n    \\n        if len(download)!=0:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Download\\\":\\\"<a href=\\\"+download+\\\">RESULT</a>\\\",\\n                \\\"Size_full\\\":size_full,\\n    \\n                })\\n        else:\\n            table.append({\\n                \\\"Set\\\":set,\\n                \\\"Photos\\\":photos,\\n                \\\"Size\\\":str(size)+\\\"MB\\\",\\n                \\\"Date\\\":set[:16],\\n                \\\"Name\\\":(set[20:-4]),\\n                \\\"Status\\\":status,\\n                \\\"Size_full\\\":size_full,\\n    \\n                })\\n    except:\\n        pass\\n\\nmsg['payload']=table\\nmsg['topic']=\\\"\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 480,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"b9a3a0f9.bcbea\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2f4c0f98.dee2\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"filelist\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"a4f09e25.02569\",\n            \"ed35109311335099\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 355,\n        \"y\": 140,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b9a3a0f9.bcbea\",\n        \"type\": \"ui_table\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"b5fdd57b.15eda8\",\n        \"name\": \"\",\n        \"order\": 1,\n        \"width\": 13,\n        \"height\": 7,\n        \"columns\": [\n            {\n                \"field\": \"Date\",\n                \"title\": \"Date\",\n                \"width\": \"150\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Name\",\n                \"title\": \"Name\",\n                \"width\": \"210\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Photos\",\n                \"title\": \"Photos\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Size\",\n                \"title\": \"Size\",\n                \"width\": \"80\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            },\n            {\n                \"field\": \"Status\",\n                \"title\": \"Status\",\n                \"width\": \"140\",\n                \"align\": \"center\",\n                \"formatter\": \"plaintext\",\n                \"formatterParams\": {\n                    \"target\": \"_blank\"\n                }\n            }\n        ],\n        \"outputs\": 1,\n        \"cts\": true,\n        \"x\": 610,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"50710948.71c308\",\n                \"4082b136.dae18\",\n                \"834046a4.647938\",\n                \"0c387c0291d6c131\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"952ce286.4ffd4\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 3,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Status\",\n        \"label\": \"Status\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 250,\n        \"y\": 60,\n        \"wires\": []\n    },\n    {\n        \"id\": \"d4383424.7807c8\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"upload\",\n        \"func\": \"import os\\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\\n\\nbasedir = '/home/pi/OpenScan/'\\n\\nif load_str(\\\"feedback_terms\\\")==\\\"False\\\":\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic'] = 'OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nmsg = msg['payload']\\n\\ndef upload(filelist, ulinks):\\n    i = 0\\n    for file in filelist:\\n        link = ulinks[i]\\n        save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\\n        cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\\\"' + file + '\\\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\\n        i = i+1\\n        os.system(cmd)\\n\\n########\\nif not os.path.isfile(basedir + 'settings/token'):\\n    msg['flag'] = True\\n    save('status_cloud', 'please enter token first')\\n    return msg\\nwith open(basedir + 'settings/token', 'r') as file:\\n    token = file.read().strip('\\\\n')\\n\\n########\\nr = OpenScanCloud('getTokenInfo', {'token':token})\\n\\nif r.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    save('status_cloud', 'invalid/missing token')\\n    return None,msg\\nelif r.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\n########\\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\\n    msg['flag'] = True\\n    save('status_cloud', 'limit(s) exceeded')\\n    return msg\\n\\n########\\ntemp =  OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\\nif temp.status_code not in (200,401):\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nif temp.status_code != 401:\\n    temp = temp.json()\\n    if 'status' in temp:\\n        if temp['status'] != 'created':\\n            save('status_cloud','already exists')\\n            with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\\n                file.write(temp['status'])\\n            return msg\\n#####\\n\\nmsg2={}\\nmsg2['token'] = token\\nmsg2['parts'] = 1\\nmsg['partslist']=[]\\n\\n#######\\nsize_to_split = load_int('osc_splitsize')\\n\\nif msg['Size_full'] > size_to_split:\\n    tempdir = basedir + 'tmp/split/'\\n    if os.path.isdir(tempdir):\\n        os.system('rm -r ' + tempdir)\\n    os.mkdir(tempdir)\\n    save('status_cloud', 'zipping files, please wait ...')\\n    cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\\n    os.system(cmd)\\n    save('status_cloud', 'zip done')\\n    list = os.listdir(tempdir)\\n    for l in list:\\n        msg['partslist'].append(tempdir + l)\\n        msg['partslist'].sort()\\n    msg2['parts']=len(msg['partslist'])\\nelse:\\n    msg['partslist'] = [basedir + 'scans/' +msg['Set']]\\n\\n#######\\nmsg2['photos'] = msg['Photos']\\nmsg2['filesize'] = msg['Size_full']\\nmsg2['project'] = msg['Set']\\n\\nr = OpenScanCloud('createProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Connection to OpenScanCloud failed'\\n    msg['payload'] = 'Please check your internet connection'\\n    return None,msg\\n\\nmsg1 = r.json()\\n\\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\\n    os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\\n    file.write('prepared')\\n\\nsave('status_cloud', 'uploading')\\nupload(msg['partslist'], msg1['ulink'])\\n\\nr = OpenScanCloud('startProject', msg2)\\nif r.status_code != 200:\\n    msg['topic'] = 'Upload failed'\\n    msg['payload'] = 'please try again'\\n    save('status_cloud', 'upload failed')\\n    return None,msg\\n\\nsave('status_cloud', 'uploaded')\\n\\nsave('status_cloud', 'project started')\\n\\ntry:\\n    os.system('rm -r ' + tempdir)\\nexcept:\\n    pass\\n\\nreturn msg\",\n        \"outputs\": 2,\n        \"x\": 530,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"9a132ab1.b21658\"\n            ],\n            [\n                \"3d16b3789632784d\",\n                \"9a132ab1.b21658\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"50710948.71c308\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"set\",\n                \"pt\": \"global\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 750,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ada1b6f7cccc9344\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"834046a4.647938\",\n        \"type\": \"ui_text\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 4,\n        \"width\": 6,\n        \"height\": 1,\n        \"name\": \"Set\",\n        \"label\": \"Set:\",\n        \"format\": \"{{msg.payload.Name}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 750,\n        \"y\": 220,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9a132ab1.b21658\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.true\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"true\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 700,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"8689e938.dd9e38\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3c67e97b.9d19a6\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable\",\n        \"func\": \"if (global.get('flag') === false){\\n    msg.enabled = false\\n    msg.color=\\\"white\\\"\\n}\\nelse{\\n    msg.enabled = true\\n    msg.color=\\\"red\\\"\\n    \\n}\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 130,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"7a93d1e18254685c\",\n                \"e434ef42bd6b92e8\",\n                \"d5d840183025d91b\",\n                \"ab9e90ab5a53a0dd\",\n                \"478994f671a3907d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bfc01f26.c32cf\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"flag.false\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"flag\",\n                \"pt\": \"global\",\n                \"to\": \"false\",\n                \"tot\": \"bool\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 420,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"f20f2dbc.0f123\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b33d604c.5f1a6\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"enable cloud\",\n        \"links\": [\n            \"4082b136.dae18\",\n            \"8689e938.dd9e38\",\n            \"e9b13dfd9f8d3711\",\n            \"f20f2dbc.0f123\",\n            \"fb13752beddee9f2\"\n        ],\n        \"x\": 35,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"3c67e97b.9d19a6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f6bd1a04.470838\",\n        \"type\": \"change\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"set\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"payload\",\n                \"pt\": \"msg\",\n                \"to\": \"set\",\n                \"tot\": \"global\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 410,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"d4383424.7807c8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4082b136.dae18\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"b33d604c.5f1a6\",\n            \"87574a42938afec4\"\n        ],\n        \"x\": 715,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f20f2dbc.0f123\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 515,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"8689e938.dd9e38\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"8367cfa0bf5bc5df\",\n            \"b33d604c.5f1a6\",\n            \"149e2e46b9623a2d\"\n        ],\n        \"x\": 795,\n        \"y\": 420,\n        \"wires\": []\n    },\n    {\n        \"id\": \"15de0ebb.616d61\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a7d89487.ee8858\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a7d89487.ee8858\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\ntry:\\n    os.remove(dir+msg['Set'])\\n    shutil.rmtree(dir+msg['Set'][:-4])\\nexcept:\\n    pass\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a4f09e25.02569\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"2f4c0f98.dee2\",\n            \"c20357dd.374108\",\n            \"e9aab326.a6896\",\n            \"edd22cc7.befe1\",\n            \"19b81967.49db87\",\n            \"8ee1b3bb.7b0b3\",\n            \"d5246b3cc796afc6\"\n        ],\n        \"x\": 775,\n        \"y\": 360,\n        \"wires\": []\n    },\n    {\n        \"id\": \"7a93d1e18254685c\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"92c98e6ce7cd25f9\"\n        ],\n        \"x\": 235,\n        \"y\": 460,\n        \"wires\": []\n    },\n    {\n        \"id\": \"4d99c601c9881680\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"refresh\",\n        \"func\": \"from time import sleep\\nimport os\\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\n\\nif load_bool(\\\"terms\\\")==False:\\n    msg['payload']=\\\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\\\"\\n    msg['topic']='OpenScanCloud - Terms of Use'\\n    return None,msg\\n\\nsave('status_cloud','refreshing')\\ntoken = load_str('token')\\n\\ntest = OpenScanCloud('getTokenInfo',{'token':token})\\nif test.status_code == 400:\\n    msg['topic'] = 'Invalid Token'\\n    msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\\n    return None,msg\\nelif test.status_code == 200:\\n    pass\\nelse:\\n    msg['topic'] = 'Connection Error'\\n    msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\\n    return None,msg\\n\\nstats = test.json()\\nfor i in stats:\\n    save('osc_'+i, stats[i])\\n    pass\\n\\nmsg={}\\nprojects = []\\nfor i in os.listdir(basepath):\\n    if os.path.isdir(basepath + i):\\n        if os.path.isfile(basepath + i + '/status'):\\n            with open(basepath + i + '/status', 'r') as file:\\n                status = file.read().strip('\\\\n')\\n                if status in ['expired', 'processing done', 'processing failed']:\\n                    continue\\n        projects.append(i)\\n\\nfor p in projects:\\n    r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\\n    if r.status_code == 200:\\n        answer = r.json()\\n        if answer == {}:\\n            os.system('sudo rm -r ' + basepath + p)\\n        else:\\n            with open(basepath + p + '/status', 'w+') as file:\\n                file.write(answer['status'])\\n            with open(basepath + p + '/download', 'w+') as file:\\n                file.write(answer['dlink'])\\n\\nmsg['list'] = projects\\nsleep(0.5)\\nsave('status_cloud','ready')\\nreturn msg, None\\n\",\n        \"outputs\": 2,\n        \"x\": 320,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"ea54fcc2.cfcc2\",\n                \"b42e061fb1f1f3d7\"\n            ],\n            [\n                \"6434e713f088012b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"372e95797a3f2f3b\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"limit :)\",\n        \"func\": \"from time import sleep\\n\\nmsg2={}\\nmsg2['enabled'] = True\\n\\nmsg['enabled'] = False\\nnode.send(msg)\\n\\nwait = 15\\n\\nfor i in range (wait):\\n    msg['text'] = ' ('+ str(wait - i)+')'\\n    node.send(msg)\\n\\nmsg['enabled'] = True\\nmsg['text']=\\\"\\\"\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 90,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"573edbfdb7500ddc\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"573edbfdb7500ddc\",\n        \"type\": \"delay\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"pauseType\": \"rate\",\n        \"timeout\": \"5\",\n        \"timeoutUnits\": \"seconds\",\n        \"rate\": \"1\",\n        \"nbRateUnits\": \"1\",\n        \"rateUnits\": \"second\",\n        \"randomFirst\": \"1\",\n        \"randomLast\": \"5\",\n        \"randomUnits\": \"seconds\",\n        \"drop\": false,\n        \"allowrate\": false,\n        \"outputs\": 1,\n        \"x\": 230,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dacb1f078b624e10\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"c8d65cc7c2ff7c36\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"92c98e6ce7cd25f9\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"7a93d1e18254685c\",\n            \"bd75f33b8a57c522\"\n        ],\n        \"x\": 35,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"c46e10b9c201913e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"3d16b3789632784d\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 690,\n        \"y\": 460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"6434e713f088012b\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Terms\",\n        \"x\": 470,\n        \"y\": 220,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"c8d65cc7c2ff7c36\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"del\",\n        \"func\": \"import os\\nimport shutil\\n\\ndir=\\\"/home/pi/OpenScan/scans/\\\"\\n\\nif msg['payload']==\\\"No\\\":\\n    return\\n\\nfor i in os.listdir(dir):\\n    if os.path.isdir(dir + i):\\n        shutil.rmtree(dir + i)\\n    else:\\n        os.remove(dir + i)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 690,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"a4f09e25.02569\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d471a5210505276\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"read\",\n        \"func\": \"var file = 'status_cloud'\\nvar file2 = 'status_uploadprogress'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nstatus = String(data);\\n\\nif (status.substr(0,9) === 'uploading'){\\n    data = fs.readFileSync(filepath+file2, 'utf8');\\n    progress = data.substr(data.length - 6)\\n        if (progress.substr(progress.length -1) === '%'){\\n            status = status + ' (' + progress + ')'\\n        }\\n}\\nmsg.status = status\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 130,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"952ce286.4ffd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f4e9a4bd79b4221f\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"dacb1f078b624e10\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2806bf08ea21216d\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.Set=global.get('set').Set\\nmsg.payload = 'Are you sure to delete the set and ALL associated files: ' + msg.Set + '?'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 410,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"15de0ebb.616d61\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"61990987acd0f263\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"6c6ef2255a7d39e5\"\n        ],\n        \"x\": 35,\n        \"y\": 60,\n        \"wires\": [\n            [\n                \"6d471a5210505276\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e8e488a6dd5d0b33\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"Download\",\n        \"order\": 5,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href={{msg.download}}>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 900,\n        \"y\": 260,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"0c387c0291d6c131\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"msg\",\n        \"func\": \"msg.download = '/scans/' + String(msg.payload.Set)\\nreturn msg;\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 750,\n        \"y\": 260,\n        \"wires\": [\n            [\n                \"e8e488a6dd5d0b33\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e5f38b4a07a5e278\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 655,\n        \"y\": 220,\n        \"wires\": [\n            [\n                \"834046a4.647938\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e434ef42bd6b92e8\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"upload2\",\n        \"order\": 6,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">upload</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"f6bd1a04.470838\",\n                \"bfc01f26.c32cf\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c46e10b9c201913e\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"refresh\",\n        \"order\": 1,\n        \"width\": 3,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">refresh{{msg.text}}</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 160,\n        \"y\": 180,\n        \"wires\": [\n            [\n                \"372e95797a3f2f3b\",\n                \"4d99c601c9881680\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d5d840183025d91b\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del set\",\n        \"order\": 8,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0, margin:0 ;color:red; width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete set</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"2806bf08ea21216d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ab9e90ab5a53a0dd\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"del \",\n        \"order\": 9,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">delete all</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 340,\n        \"wires\": [\n            [\n                \"f4e9a4bd79b4221f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"478994f671a3907d\",\n        \"type\": \"ui_template\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"group\": \"db43d646.2074c8\",\n        \"name\": \"combine\",\n        \"order\": 7,\n        \"width\": 2,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"color:red; padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">combine</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 280,\n        \"y\": 500,\n        \"wires\": [\n            [\n                \"51bfd0fb7b1d292e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"189c1eed09624a7b\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"combine = global.get('combine')\\ncombine_set = global.get('set').Set\\n\\nif (combine === true && global.get('combine_set') !== combine_set){\\n    msg.set1 = global.get('combine_set')\\n    msg.set2 = combine_set\\n    global.set('combine', false)\\n    msg.topic = 'Combine the following two sets:'\\n    msg.payload = msg.set1 + '<br>' + msg.set2 + '<br>FILES WILL BE MERGED INTO ON FILE!'\\n    return msg\\n}\\nglobal.set('combine_set' , combine_set)\\n\\n\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 280,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"1493398979a63775\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51bfd0fb7b1d292e\",\n        \"type\": \"function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"global.set('combine', true)\\ncombine_set = global.get('set').Set\\nmsg.topic = 'Merge two sets into one (can not be undone)!'\\nmsg.payload = combine_set\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 420,\n        \"y\": 500,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"da325be8e74179be\",\n        \"type\": \"python3-function\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"func\": \"from os.path import getsize\\nfrom shutil import copy\\nfrom os import rename, remove\\nimport zipfile as z\\nfrom OpenScan import save\\n\\nfrom time import sleep\\n\\nif msg['payload'] != 'OK':\\n    return\\n\\nbasepath = '/home/pi/OpenScan/scans/'\\ntmp1 = basepath + msg['set1']\\ntmp2 = basepath + msg['set2']\\n\\nif getsize(tmp1) > getsize(tmp2):\\n    set1 = tmp1\\n    set2 = tmp2\\nelse:\\n    set1 = tmp2\\n    set2 = tmp1\\n\\n#set 1 is larger and to be merged into\\n\\nzips = [set1, set2]\\n\\nwith z.ZipFile(set1, 'a') as z1:\\n    z2 = z.ZipFile(set2, 'r')\\n    i = 0\\n    for n in z2.namelist():\\n        i += 1\\n        n2 = n\\n        save('status_cloud','combining ' + str(i) + '/' + str(len(z2.namelist())))\\n        while 'X'+n in z1.namelist():\\n            n = 'X' + n\\n        z1.writestr('X'+n, z2.open(n2).read())\\nsave('status_cloud','ready')\\n\\nos.rename(set1, set1[:-4] + 'X.zip')\\nos.remove(set2)\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 560,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"ed35109311335099\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ed35109311335099\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"809c9427e14e2448\",\n            \"2f4c0f98.dee2\"\n        ],\n        \"x\": 655,\n        \"y\": 540,\n        \"wires\": []\n    },\n    {\n        \"id\": \"1493398979a63775\",\n        \"type\": \"ui_toast\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Combine\",\n        \"x\": 420,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"da325be8e74179be\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ada1b6f7cccc9344\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"6dd356510c446cf4\"\n        ],\n        \"x\": 835,\n        \"y\": 180,\n        \"wires\": []\n    },\n    {\n        \"id\": \"6dd356510c446cf4\",\n        \"type\": \"link in\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"combine\",\n        \"links\": [\n            \"ada1b6f7cccc9344\"\n        ],\n        \"x\": 175,\n        \"y\": 540,\n        \"wires\": [\n            [\n                \"189c1eed09624a7b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b42e061fb1f1f3d7\",\n        \"type\": \"link out\",\n        \"z\": \"4981d84ef1a366d1\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"397ab7f44b893c89\"\n        ],\n        \"x\": 435,\n        \"y\": 140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"52858b4eceacc902\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 4,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Terms\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 90,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"f99ec8781a33ec7d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7dc39bd847d16ded\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Agree\",\n        \"cancel\": \"Disagree\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 410,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"5f849178998d9082\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cc3cb10f2ea3f8b8\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"blink Light1\",\n        \"func\": \"import RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nfrom OpenScan import ringlight\\nfrom time import sleep\\n\\ndelay = 0.1\\nringlight(2,False)\\n\\nfor i in range (5):\\n    ringlight(1,True)\\n    sleep(delay)\\n    ringlight(1,False)\\n    sleep(delay)\",\n        \"outputs\": 1,\n        \"x\": 310,\n        \"y\": 420,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d114f4d4d7f31981\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"func\": \"import os\\nfrom time import sleep\\nsleep(1.5)\\nimport RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nos.system('sudo reboot')\\n\",\n        \"outputs\": 1,\n        \"x\": 290,\n        \"y\": 380,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"f52d4d86b39aeb6b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"shutdown\",\n        \"func\": \"import os\\nfrom time import sleep\\nsleep(1.5)\\nimport RPi.GPIO as GPIO\\nGPIO.setwarnings(False)\\nGPIO.cleanup()\\nos.system('sudo shutdown -h now')\",\n        \"outputs\": 1,\n        \"x\": 300,\n        \"y\": 460,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"15a0a2f431ce55c3\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"General Settings\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 260,\n        \"wires\": []\n    },\n    {\n        \"id\": \"87a403b9a09aa38d\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Network\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"ca4afadb5b21751f\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Info Texts\",\n        \"info\": \"\",\n        \"x\": 100,\n        \"y\": 120,\n        \"wires\": []\n    },\n    {\n        \"id\": \"2a0f9919.4c9a86\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OpenScanCloud\",\n        \"info\": \"\",\n        \"x\": 140,\n        \"y\": 880,\n        \"wires\": []\n    },\n    {\n        \"id\": \"27c6b221c90ed9e1\",\n        \"type\": \"exec\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"command\": \"sudo iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\\\"//g;s/^ *//;s/ *$//'\",\n        \"addpay\": false,\n        \"append\": \"\",\n        \"useSpawn\": \"false\",\n        \"timer\": \"\",\n        \"winHide\": false,\n        \"oldrc\": false,\n        \"name\": \"scan\",\n        \"x\": 270,\n        \"y\": 720,\n        \"wires\": [\n            [\n                \"b05cf92302a5c112\"\n            ],\n            [\n                \"e9677b85856b5873\"\n            ],\n            []\n        ]\n    },\n    {\n        \"id\": \"b05cf92302a5c112\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"WIFI\",\n        \"func\": \"msg['options']=[]\\n\\nfor i in msg['payload'].split('\\\\n'):\\n    if i not in msg['options'] and i!=\\\"\\\":\\n        msg['options'].append(i)\\n        \\nif len(msg['options']) != 0:\\n    msg['enabled']=True\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 390,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"da5ddaf4cc25b8c8\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"search\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 11,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Search Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"true\",\n        \"payloadType\": \"bool\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 110,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\",\n                \"51521bc6eb44cde5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"59c9f67283ba1709\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select Wifi\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 14,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"multiple\": false,\n        \"options\": [],\n        \"payload\": \"\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"className\": \"\",\n        \"x\": 540,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"2bb52656f9554dab\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"b2d7d6a730f7dca6\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 12,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Reset Wifi\",\n        \"tooltip\": \"\",\n        \"color\": \"red\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)\",\n        \"payloadType\": \"str\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 130,\n        \"y\": 820,\n        \"wires\": [\n            [\n                \"78985ac6d3bcdf60\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c3b8faac9ebb2c80\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset Wifi\",\n        \"func\": \"from time import sleep\\n\\nif msg['payload']!=\\\"Yes\\\":\\n    return\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\nwith open(temp_dir, 'w+') as file:\\n    file.write('update_config=1\\\\nctrl_interface=DIR=/var/run/wpa_supplicant\\\\ncountry=de\\\\n\\\\n')\\nos.system('sudo mv '+ temp_dir + ' ' + wpa_dir)\\nos.system('sudo wpa_cli -i wlan0 reconfigure')\\nsleep(3)\\nos.system('sudo systemctl restart nodered')\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 460,\n        \"y\": 820,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"78985ac6d3bcdf60\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"No\",\n        \"cancel\": \"Yes\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 290,\n        \"y\": 820,\n        \"wires\": [\n            [\n                \"c3b8faac9ebb2c80\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4f7f49b12c2d2572\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"add Wifi\",\n        \"func\": \"from time import sleep\\nsleep(0.1)\\n\\nos.system('sudo wpa_cli -i wlan0 reconfigure')\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 1340,\n        \"y\": 680,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ebcc98685059b9d4\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"password\",\n        \"x\": 800,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"68204a14528ab842\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"68204a14528ab842\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['password'] = msg['payload']\\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: <a href=\\\"https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\\\">Wikipedia</a>)'\\n\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 930,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"852edf901bdec9c5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"852edf901bdec9c5\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"Save\",\n        \"cancel\": \"Cancel\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"country\",\n        \"x\": 1060,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"1b09d634e3d9357b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1b09d634e3d9357b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"modWPA\",\n        \"func\": \"if msg['payload'] == 'Cancel':\\n    return\\n\\nif len(msg['payload'])!=2:\\n    msg['payload'] = 'invalid country code'\\n    return msg,None\\n\\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\\n\\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\\n\\ncode = msg['payload'].upper()\\nssid = msg['ssid']\\npassword = msg['password']\\n\\nif len(code) != 2:\\n    msg['topic'] = 'ERROR'\\n    msg['payload'] = 'invalid country code (see  ISO 3166-1 alpha-2)'\\n    return msg\\n\\nwith open(wpa_dir, 'r') as file:\\n    for i in file.readlines():\\n        if 'country=' in i:\\n            code_old=i.split('country=')[1][0:2]\\n            break\\n\\nwith open(wpa_dir, 'r') as file:\\n    wpa = file.read()\\n    if ssid in wpa:\\n        msg['topic'] = 'ERROR'\\n        msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\\n        return msg\\n    wpa=wpa.replace('country=' + code_old, 'country=' + code)\\n    wpa=wpa + '\\\\nnetwork={\\\\n priority=10\\\\n ssid=\\\"'+ssid+'\\\"\\\\n psk=\\\"'+password+'\\\"\\\\n}\\\\n'\\n\\nwith open(temp_dir,'w+') as file:\\n    file.write(wpa)\\nos.system('sudo mv '+temp_dir + ' ' + wpa_dir)\\n\\nmsg['topic'] = 'Updating Wifi'\\nmsg['payload'] = 'reconnecting might take a moment'\\nreturn msg,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 1200,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ],\n            [\n                \"4f7f49b12c2d2572\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"03732a7d3b0c95aa\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1350,\n        \"y\": 640,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"e97d17c6590138e2\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Cloud\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 2,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">OpenScanCloud</h1> <p>OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at <a href=\\\"https://www.buymeacoffee.com/OpenScan\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">BuyMeACoffee</a>.</p> <p>The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.</p> <p><strong>Register</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.</p> <p><strong>Enter Token</strong></p> <p style=\\\"margin-left: 20px;\\\">Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact <a href=\\\"mailto:cloud@openscan.eu\\\">cloud@openscan.eu</a></p> <p><strong>Terms</strong></p> <p style=\\\"margin-left: 20px;\\\">Please read the terms of use to understand what will happen to your data, when using the OpenScanCloud service.</p> <p><strong>Token</strong></p> <p style=\\\"margin-left: 20px;\\\">A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests <a href=\\\"mailto:cloud@openscan.eu\\\">cloud@openscan.eu</a></p> <p><strong>Credit (GB)</strong></p> <p style=\\\"margin-left: 20px;\\\">Each token comes with a given amount of &apos;credit&apos; which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers.&nbsp;</p> <p style=\\\"margin-left: 20px;\\\">IMPORTANT: The credit can be increased at any time by sending a (nice) mail to <a href=\\\"mailto:cloud@openscan.eu\\\">cloud@openscan.eu</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 250,\n        \"y\": 120,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1969c709ef2fd1d5\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 7,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Credit (GB):\",\n        \"format\": \"{{msg.credit}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 730,\n        \"y\": 1140,\n        \"wires\": []\n    },\n    {\n        \"id\": \"397ab7f44b893c89\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSCparameters\",\n        \"links\": [\n            \"960912e90ba5b5bc\",\n            \"9c51aa678f16980f\",\n            \"b42e061fb1f1f3d7\"\n        ],\n        \"x\": 465,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"a7fd00943edc380b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bf6d941ad307ce22\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 270,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"f22dfef37d5de773\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f22dfef37d5de773\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import re\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmail = msg['payload']\\nemail_regex = re.compile(r\\\"[^@]+@[^@]+\\\\.[^@]+\\\")\\n\\nif email_regex.match(mail) != None:\\n    msg['mail'] = mail\\n    msg['topic'] = 'OpenScanCloud Registration (2/3)'\\n    msg['payload'] = 'Enter your first name'\\n    return msg\\nmsg['payload'] = 'invalid input'\\nreturn None,msg\\n\",\n        \"outputs\": 2,\n        \"x\": 410,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"54602ee49ca022e7\"\n            ],\n            [\n                \"1505f3e72f971081\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1505f3e72f971081\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 1000,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"54602ee49ca022e7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 550,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"f9efcb87b74abbd4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"510dbe4d76253bd6\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"SUBMIT\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 830,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"600b2306caed1640\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"600b2306caed1640\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import OpenScanCloud\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\nmsg['lastname'] = msg['payload']\\n\\nmsg2 = {}\\n\\nfor i in ['forename','lastname','mail']:\\n    msg2[i] = msg[i]\\n\\nr = OpenScanCloud('requestToken',msg2)\\n\\nstatus = r.status_code\\n\\nmsg['topic'] = 'OpenScanCloud Registration - Success'\\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\\n\\nif status != 200:\\n    msg['topic'] = 'OpenScanCloud Registration - Failed'\\n    msg['payload'] = 'Registration failed, please try again.'\\n\\nmsg['status'] = status\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 970,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"bbad1ab5f8f63fb7\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d34cd203725bac15\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Register\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 5,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Register\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Please enter your email address:\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Requesting an OpenScanCloud Token\",\n        \"topicType\": \"str\",\n        \"x\": 120,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"bf6d941ad307ce22\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"bbad1ab5f8f63fb7\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 1110,\n        \"y\": 960,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"a7fd00943edc380b\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"from OpenScan import load_int\\n\\nmsg = {}\\n\\ntry:\\n    msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\\n    return msg\\nexcept:\\n    pass\",\n        \"outputs\": 1,\n        \"x\": 590,\n        \"y\": 1140,\n        \"wires\": [\n            [\n                \"1969c709ef2fd1d5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f99ec8781a33ec7d\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.payload = 'This is a free piece of software and it is provided as is, without any warranty. <br>There might be functions that need a connection to the internet: '+\\n    '<br><br>By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\\n    'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\\n    'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\\n    '<br><br>By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\\n    'The raw images and resulting 3d models will never be published without your explicit consent.'+  \\n    '<br><br>If you have any questions you can contact me at info@openscan.eu.'+ \\n    '<br><br>THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\\n    'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \\n    'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \\n    'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 300,\n        \"wires\": [\n            [\n                \"7dc39bd847d16ded\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5f849178998d9082\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"if(msg.payload === 'Agree'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nvar file = 'terms'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nfs.writeFile(filepath+file, String(data), err => {\\n  if (err) {\\n    return msg\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 550,\n        \"y\": 300,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"725fd0cab0bddc0e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'hostname'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst data = fs.readFileSync(filepath+file, 'utf8');\\nmsg.payload = String(data);\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"49259adad52fc214\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"49259adad52fc214\",\n        \"type\": \"ui_text_input\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Hostname\",\n        \"tooltip\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 13,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"mode\": \"text\",\n        \"delay\": \"0\",\n        \"topic\": \"Change hostname to:\",\n        \"sendOnBlur\": true,\n        \"className\": \"\",\n        \"topicType\": \"str\",\n        \"x\": 550,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"8001f7c361de7d8c\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"51521bc6eb44cde5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"msg.enabled = false\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 270,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"59c9f67283ba1709\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2bb52656f9554dab\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"ssid = msg.payload\\nmsg.topic = 'Add wifi network (' + ssid + ')'\\nmsg.payload = 'Enter Wifi password:'\\nmsg.ssid = ssid\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 670,\n        \"y\": 660,\n        \"wires\": [\n            [\n                \"ebcc98685059b9d4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"ebce67b739d1891f\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk/change hostname\",\n        \"func\": \"from OpenScan import save\\n\\nif msg['payload'] != 'OK':\\n    pass\\n\\nwith open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\n\\nhostname = msg['hostname']\\nif len(hostname) < 4 :\\n    msg['payload'] = ' '\\n    msg['topic'] = 'ERROR - Hostname NOT changed'\\n    return msg\\n    \\n\\nwith open('/etc/hostname', 'w+') as file:\\n    file.write(hostname)\\nos.system('echo ' + hostname + ' | sudo tee /etc/hostname')\\nwith open('/etc/hosts', 'r') as file:\\n    temp = file.read()\\ntemp = temp.replace(old_hostname,hostname)\\nwith open('/etc/hosts', 'w') as file:\\n    file.write(temp)\\nos.system('sudo hostnamectl set-hostname ' + hostname)\\nos.system('sudo systemctl restart avahi-daemon')\\nsave('hostname',hostname)\\nmsg['payload'] = hostname\\nmsg['topic'] = 'Success - Hostname changed'\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 1160,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"03732a7d3b0c95aa\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"667ac2aba819f506\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Confirm\",\n        \"x\": 940,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8001f7c361de7d8c\",\n        \"type\": \"change\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"rules\": [\n            {\n                \"t\": \"set\",\n                \"p\": \"hostname\",\n                \"pt\": \"msg\",\n                \"to\": \"payload\",\n                \"tot\": \"msg\"\n            }\n        ],\n        \"action\": \"\",\n        \"property\": \"\",\n        \"from\": \"\",\n        \"to\": \"\",\n        \"reg\": false,\n        \"x\": 730,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"667ac2aba819f506\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9bb0adbd716ce347\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"reboot\",\n        \"links\": [\n            \"16c76929f88df841\",\n            \"fe3a855fee9e28c6\",\n            \"d663dd83d71b8693\"\n        ],\n        \"x\": 175,\n        \"y\": 380,\n        \"wires\": [\n            [\n                \"d114f4d4d7f31981\",\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"fc9abb94c35eec56\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"shutdown\",\n        \"links\": [\n            \"597bfb653e8cddbf\"\n        ],\n        \"x\": 175,\n        \"y\": 460,\n        \"wires\": [\n            [\n                \"cc3cb10f2ea3f8b8\",\n                \"f52d4d86b39aeb6b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f9efcb87b74abbd4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload === 'Cancel'){\\n    return\\n}\\nmsg.forename = msg.payload\\nmsg.topic = 'OpenScanCloud Registration (3/3)'\\nmsg.payload = 'Enter your last name'\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 690,\n        \"y\": 960,\n        \"wires\": [\n            [\n                \"510dbe4d76253bd6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"adc206aa8edd1e41\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"OSC\",\n        \"group\": \"db43d646.2074c8\",\n        \"order\": 2,\n        \"width\": 3,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Files&amp;Cloud</h1> <p><strong>Refresh</strong></p> <p style=\\\"margin-left: 20px;\\\">You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.</p> <p style=\\\"margin-left: 20px;\\\">The status (in the table) of the individual sets in the file list will be updated to one of the following:</p> <p style=\\\"margin-left: 20px;\\\"><u>Created </u>- you started the upload of your image set. If you are stuck on this status, please try to restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Initialized&nbsp;</u>- all files have been uploaded and processing will start as soon as possible</p> <p style=\\\"margin-left: 20px;\\\"><u>File approved </u>- the server received and verified your files</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing started</u> - your files are currently being processed</p> <p style=\\\"margin-left: 20px;\\\"><u>Processing failed</u> - there are various reasons why processing might fail. Please check the email for more details or contact me at <a href=\\\"mailto:cloud@openscan.eu\\\">cloud@openscan.eu</a></p> <p style=\\\"margin-left: 20px;\\\">processing done - check your email, where you should find a link to the 3d model :)</p> <p><strong>Status (on the right column)</strong></p> <p style=\\\"margin-left: 20px;\\\">Indicates, what the device is currently up to.</p> <p style=\\\"margin-left: 20px;\\\"><u>Refreshing&nbsp;</u>- updating all image set&apos;s status</p> <p style=\\\"margin-left: 20px;\\\"><u>Uploading&nbsp;</u>- while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.</p> <p style=\\\"margin-left: 20px;\\\"><u>Project started</u> - when the upload of a set was successful</p> <p style=\\\"margin-left: 20px;\\\"><u>Zipping&nbsp;</u>- files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.</p> <p style=\\\"margin-left: 20px;\\\"><u>Combining</u> - two sets into one might take up to a minute.&nbsp;</p> <p><strong>Set</strong></p> <p style=\\\"margin-left: 20px;\\\">select a set from the file list by clicking on a row in the table</p> <p><strong>Download</strong></p> <p style=\\\"margin-left: 20px;\\\">Download the selected set from the OpenScan device to your computer/mobile/tablet</p> <p><strong>Upload</strong></p> <p style=\\\"margin-left: 20px;\\\">Upload the selected file to the OpenScanCloud</p> <p><strong>Combine</strong></p> <p style=\\\"margin-left: 20px;\\\">In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!</p> <p><strong>Delete Set/All</strong></p> <p style=\\\"margin-left: 20px;\\\">Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 470,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e9677b85856b5873\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Reset rfkill\",\n        \"func\": \"from os import system\\nif \\\"Interface doesn't support scanning\\\" in msg['payload']:\\n    system('rfkill unblock all')\\n    system('sudo ifconfig wlan0 up')\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 410,\n        \"y\": 740,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"9b2bc9849aee310b\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"changeHostname\",\n        \"links\": [\n            \"ec2db55a99bbe3ee\",\n            \"d5175561293ef490\",\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 855,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"8b9e3781511e9231\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"8b9e3781511e9231\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"chk\",\n        \"func\": \"with open('/etc/hostname', 'r') as file:\\n    old_hostname = file.read().replace('\\\\n','')\\nif old_hostname == 'raspberrypi':\\n    msg['hostname'] = 'openscan'\\n    msg['payload'] = 'OK'\\n    return msg\",\n        \"outputs\": 1,\n        \"x\": 950,\n        \"y\": 580,\n        \"wires\": [\n            [\n                \"ebce67b739d1891f\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"65b38bfeb3fee710\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 175,\n        \"y\": 420,\n        \"wires\": [\n            [\n                \"cc3cb10f2ea3f8b8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d3fc91d87d5d5f62\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 155,\n        \"y\": 620,\n        \"wires\": [\n            [\n                \"725fd0cab0bddc0e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"cc9c4092edeb43cc\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"enable projectname\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 155,\n        \"y\": 700,\n        \"wires\": [\n            [\n                \"27c6b221c90ed9e1\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"80bccc884b0be297\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"38783aea9cc317a6\"\n        ],\n        \"x\": 1435,\n        \"y\": 300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"25426d3582cc1236\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Enter Tk\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 3,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"Enter Token\",\n        \"tooltip\": \"testtesttest\",\n        \"color\": \"\",\n        \"bgcolor\": \"\",\n        \"className\": \"\",\n        \"icon\": \"\",\n        \"payload\": \"Please enter your OpenScanCloud Token:\",\n        \"payloadType\": \"str\",\n        \"topic\": \"Token\",\n        \"topicType\": \"str\",\n        \"x\": 120,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"c690fed61878ce83\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"c690fed61878ce83\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"prompt\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"Cancel\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 270,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"781f672b78ea70b2\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"781f672b78ea70b2\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"save\",\n        \"func\": \"import requests\\nimport os\\nfrom OpenScan import save, OpenScanCloud\\n\\nif msg['payload'] == 'Cancel':\\n    return\\n\\ntoken = msg['payload']\\n\\nif len(msg['payload']) >=14:\\n    try:\\n        r = OpenScanCloud('getTokenInfo', {'token':token})\\n        if r.status_code != 200:\\n            msg['topic'] = 'Error'\\n            msg['payload'] = 'Invalid Token'\\n            return msg \\n        \\n        msg1 = r.json()\\n        save('osc_credit',msg1['credit'])\\n        save('osc_limit_filesize',msg1['limit_filesize'])\\n        save('osc_limit_photos',msg1['limit_photos'])\\n        save('token',token)\\n        msg['topic'] = 'Success'\\n        msg['payload'] = 'Token verified and saved'\\n    except:\\n        msg['topic'] = 'Error'\\n        msg['payload'] = 'Could not verify token, please check your internet connection.'\\n        return msg \\n\\n\\nelse:\\n    msg['topic'] = 'Error'\\n    msg['payload'] = 'Invalid tokenformat'\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 430,\n        \"y\": 1060,\n        \"wires\": [\n            [\n                \"5e4b3bdb0a26052d\",\n                \"4faf2fbd3cf6aa3a\",\n                \"a7fd00943edc380b\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"6d2c65d7e1d928ce\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 6,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Token\",\n        \"format\": \"{{msg.payload}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 710,\n        \"y\": 1100,\n        \"wires\": []\n    },\n    {\n        \"id\": \"5e4b3bdb0a26052d\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"text\",\n        \"func\": \"from OpenScan import load_str\\n\\ntoken = load_str('token')[0:8]\\nmsg['payload']= token + '...'\\nif len(token)==0:\\n    msg['payload']=\\\"enter token\\\"\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 590,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"6d2c65d7e1d928ce\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"e0965e490d53617f\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"token\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 465,\n        \"y\": 1100,\n        \"wires\": [\n            [\n                \"5e4b3bdb0a26052d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"4faf2fbd3cf6aa3a\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 610,\n        \"y\": 1060,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"36b3b36c399ac7db\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"get update\",\n        \"func\": \"import json\\nimport requests\\nfrom OpenScan import load_str\\n\\nif not msg['payload']:\\n    msg['status'] = '--READY--'\\n    return msg\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'no internet connection'\\n    return msg\\n\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg['status'] = 'checking updates'\\n\\nreturn msg, msg\",\n        \"outputs\": 2,\n        \"x\": 310,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"1d9f24f41817a2de\"\n            ],\n            [\n                \"0c1d054fa7f2afe8\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"48cd023b07c39a94\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 20,\n        \"width\": 0,\n        \"height\": 0,\n        \"name\": \"\",\n        \"label\": \"Status:\",\n        \"format\": \"{{msg.status}}\",\n        \"layout\": \"row-spread\",\n        \"className\": \"\",\n        \"x\": 170,\n        \"y\": 1300,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0c1d054fa7f2afe8\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"check files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str\\nfrom time import sleep\\n\\nsleep(1)\\n\\n\\nscope = load_str('update_type')\\nmsg['scope'] = scope\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_old = updatepath + 'update.json'\\n\\ncounter = 0\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    if os.path.isfile(temp):\\n        filesize = os.path.getsize(temp)\\n        msg[scope][i]['filesize2'] = filesize\\n        if filesize == msg[scope][i]['filesize']:\\n            msg[scope][i]['update'] = False\\n            continue\\n    msg[scope][i]['update'] = True\\n\\n    counter += 1\\n\\nif counter == 0:\\n    msg['status'] = 'No new update available'\\nelse:\\n    msg['status'] = 'New update available'\\n    msg['topic'] = msg['status']\\n    msg['payload'] = 'Install & reboot now?'\\n\\nmsg['counter'] = counter # new file counter\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 470,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"1d9f24f41817a2de\",\n                \"7097687ddcc4fa8e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"612a7556ab11cf7d\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"links\": [\n            \"960912e90ba5b5bc\"\n        ],\n        \"x\": 75,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"e447af84ecc540ad\",\n                \"72ca6c281c43acd7\",\n                \"dadf823225aa34c4\",\n                \"9df2481a03f24d0a\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7097687ddcc4fa8e\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"NO\",\n        \"cancel\": \"YES\",\n        \"raw\": false,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"\",\n        \"x\": 630,\n        \"y\": 1420,\n        \"wires\": [\n            [\n                \"f9fe26a38501bcad\",\n                \"77859c0059f8a49e\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"9112e8b2865ea436\",\n        \"type\": \"link in\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"update status\",\n        \"links\": [\n            \"1d9f24f41817a2de\",\n            \"26dae88a383eee97\"\n        ],\n        \"x\": 75,\n        \"y\": 1300,\n        \"wires\": [\n            [\n                \"48cd023b07c39a94\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"1d9f24f41817a2de\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9112e8b2865ea436\"\n        ],\n        \"x\": 575,\n        \"y\": 1380,\n        \"wires\": []\n    },\n    {\n        \"id\": \"f9fe26a38501bcad\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"download files\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\nfrom OpenScan import load_str\\n\\n\\nscope = msg['scope']\\n\\nupdatepath = '/home/pi/OpenScan/updates/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\n\\nif msg['payload'] != 'YES':\\n    return\\n\\ncounter = 0\\n\\nfor i in msg[scope]:\\n    if msg[scope][i]['update'] == False:\\n        continue\\n    \\n    filepath = msg[scope][i]['dst']\\n    temp = updatepath + os.path.basename(filepath)\\n    \\n    r = requests.get(url + msg[scope][i]['src'])\\n    if r.status_code != 200:\\n        msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\\n        return msg\\n    with open(temp, 'wb+') as file:\\n        file.write(r.content)\\n    shutil.copy(temp, msg[scope][i]['dst'])\\n    counter += 1\\n\\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\\n\\nif counter == msg['counter']:\\n    updatepath_temp = updatepath + 'update_temp.json'\\n    updatepath_old = updatepath + 'update.json'\\n    shutil.move(updatepath_temp, updatepath_old)\\n\\nreturn msg\\n\",\n        \"outputs\": 1,\n        \"x\": 800,\n        \"y\": 1440,\n        \"wires\": [\n            [\n                \"26dae88a383eee97\",\n                \"d663dd83d71b8693\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"77859c0059f8a49e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"msg\",\n        \"func\": \"if (msg.payload == 'YES'){\\n    msg.status = 'Installing updates'\\n    return msg}\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 770,\n        \"y\": 1400,\n        \"wires\": [\n            [\n                \"26dae88a383eee97\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"26dae88a383eee97\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9112e8b2865ea436\"\n        ],\n        \"x\": 925,\n        \"y\": 1400,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e447af84ecc540ad\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\n\\nmsg.payload = data\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 1380,\n        \"wires\": [\n            [\n                \"36b3b36c399ac7db\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d663dd83d71b8693\",\n        \"type\": \"link out\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"mode\": \"link\",\n        \"links\": [\n            \"9bb0adbd716ce347\"\n        ],\n        \"x\": 925,\n        \"y\": 1440,\n        \"wires\": []\n    },\n    {\n        \"id\": \"444acd32e7578254\",\n        \"type\": \"python3-function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"create beta new\",\n        \"func\": \"import json\\nimport requests\\nimport shutil\\n\\n#scope = 'main'\\nscope = 'mini'\\n\\nupdatepath = '/home/pi/OpenScan/tmp/'\\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\\nupdatepath_temp = updatepath + 'update_temp.json'\\nupdatepath_old = updatepath + 'update.json'\\n\\n\\n## load update.json\\nr = requests.get(url + 'update.json')\\n\\nif r.status_code != 200:\\n    msg['status'] = 'download update.json failed'\\n    return msg\\n\\nmsg = {}\\nwith open(updatepath_temp, 'wb+') as file:\\n    file.write(r.content)\\nwith open(updatepath_temp, 'r') as file:\\n    msg = json.load(file)\\n\\nmsg2 = msg.copy()\\ntry:\\n    del msg[scope]\\nexcept:\\n    pass\\n\\nmsg[scope]={}\\nmsg[scope]['1'] = {}\\nmsg[scope]['1']['src'] = scope + '/fla.py'\\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\\n\\nmsg[scope]['2'] = {}\\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\\n\\nmsg[scope]['3'] = {}\\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\\n\\nmsg[scope]['4'] = {}\\nmsg[scope]['4']['src'] = scope + '/config.txt'\\nmsg[scope]['4']['dst'] = '/boot/config.txt'\\n\\nmsg[scope]['5'] = {}\\nmsg[scope]['5']['src'] = scope + '/flows.json'\\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\\n\\nmsg[scope]['6'] = {}\\nmsg[scope]['6']['src'] = scope + '/settings.js'\\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\\n\\nmsg[scope]['7'] = {}\\nmsg[scope]['7']['src'] = 'files/logo.jpg'\\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\\n\\nfor i in msg[scope]:\\n    filepath = msg[scope][i]['dst']\\n    filesize = os.path.getsize(filepath)\\n    msg[scope][i]['filesize'] = filesize\\n\\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\\n    os.system('rm -r /home/pi/OpenScan/tmp/update')    \\nos.makedirs('/home/pi/OpenScan/tmp/update/')\\n\\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\\n    json.dump(msg, f, indent=4)\\n\\nfor i in msg[scope]:\\n    if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\\n        os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\\n    shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\\n\\nmsg['payload'] = 'created with scope: ' + scope\\n\\nreturn msg\",\n        \"outputs\": 1,\n        \"x\": 280,\n        \"y\": 1780,\n        \"wires\": [\n            [\n                \"7f097823a90facb6\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"7f097823a90facb6\",\n        \"type\": \"debug\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"active\": true,\n        \"tosidebar\": true,\n        \"console\": false,\n        \"tostatus\": false,\n        \"complete\": \"true\",\n        \"targetType\": \"full\",\n        \"statusVal\": \"\",\n        \"statusType\": \"auto\",\n        \"x\": 430,\n        \"y\": 1780,\n        \"wires\": []\n    },\n    {\n        \"id\": \"e547e40ff805742b\",\n        \"type\": \"inject\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"props\": [\n            {\n                \"p\": \"payload\"\n            },\n            {\n                \"p\": \"topic\",\n                \"vt\": \"str\"\n            }\n        ],\n        \"repeat\": \"\",\n        \"crontab\": \"\",\n        \"once\": false,\n        \"onceDelay\": 0.1,\n        \"topic\": \"\",\n        \"payload\": \"\",\n        \"payloadType\": \"date\",\n        \"x\": 120,\n        \"y\": 1780,\n        \"wires\": [\n            [\n                \"444acd32e7578254\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"5fe2d831c3ab1cf4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 570,\n        \"y\": 1340,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"154716c51aae2b87\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Auto-check update availability\",\n        \"tooltip\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 21,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 370,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"5fe2d831c3ab1cf4\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"72ca6c281c43acd7\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'update_auto'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 1340,\n        \"wires\": [\n            [\n                \"154716c51aae2b87\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"a0e996cbd2b18363\",\n        \"type\": \"comment\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Updates\",\n        \"info\": \"\",\n        \"x\": 120,\n        \"y\": 1240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"0dcf979b126c3e33\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 1,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"OPENSCANCLOUD\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 290,\n        \"y\": 900,\n        \"wires\": []\n    },\n    {\n        \"id\": \"50ab351d92165de8\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 9,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"NETWORK SETTINGS\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 560,\n        \"wires\": []\n    },\n    {\n        \"id\": \"726819d40397f3ce\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Wifi\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 10,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Network Settings</h1> <p><strong>Hostname</strong></p> <p style=\\\"margin-left: 20px;\\\">The device can be accessed through any browser in the same network. Therefore, you can either enter the device&apos;s IP address or the given hostname. The standard name is &apos;openscan&apos; but it is highly recommended to change the name, when using multiple devices (e.g. &apos;openscan1&apos;, &apos;openscan2&apos; ...)</p> <p><strong>Select Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: <a href=\\\"https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">ISO 3166 Country Code on Wikipedia</a></p> <p><strong>Search Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">You can manually refresh the list of available networks by pressing this button.</p> <p><strong>Reset Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see <a href=\\\"https://www.glennklockwood.com/sysadmin-howtos/rpi-headless-boot.html\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">glennklockwood.com</a> for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 250,\n        \"y\": 160,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"d07e9c092f0855eb\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 16,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"UPDATES\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 270,\n        \"y\": 1240,\n        \"wires\": []\n    },\n    {\n        \"id\": \"a85de9dee94dc786\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Updates&LOG\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 17,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Update&amp;Log</h1> <p><strong>Status</strong></p> <p style=\\\"margin-left: 20px;\\\">See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a> for details and the source code.</p> <p><strong>Auto-Check update availability</strong></p> <p style=\\\"margin-left: 20px;\\\">Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a></p> <p style=\\\"margin-left: 20px;\\\">This option is activated by default.</p> <p><strong>Check Updates</strong></p> <p style=\\\"margin-left: 20px;\\\">Alternatively, you can check for updates manually at any time by pressing this button.</p> <p><strong>Download Error Log</strong></p> <p style=\\\"margin-left: 20px;\\\">In case you encounter any errors with your device, please download the error log text and send a copy to <a href=\\\"mailto:info@openscan.eu\\\">info@openscan.eu</a> or create an issue on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a></p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 280,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"2968c5996fb6d98c\",\n        \"type\": \"ui_template\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"name\": \"Download LOG\",\n        \"order\": 25,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<md-button style=\\\"background-color:default ; color:default;width:100%; height:100%;\\\"   target=\\\"_blank\\\" ng-href='tmp/log.txt'>\\n <div class=\\\"center\\\" style=\\\"position: relative; top: 30%;\\\">Download error log\\n</div></md-button>\\n\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 140,\n        \"y\": 1520,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"d66c08c5f0134cf3\",\n        \"type\": \"ui_template\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"name\": \"Check Updates\",\n        \"order\": 18,\n        \"width\": 6,\n        \"height\": 1,\n        \"format\": \"<md-button class=\\\"button save\\\" style=\\\"padding:0 0; margin:0 ;width:100%; height:100%;\\\"ng-click=\\\"send({payload:'1'})\\\">Check updates</md-button></md-button>\",\n        \"storeOutMessages\": false,\n        \"fwdInMessages\": false,\n        \"resendOnRefresh\": false,\n        \"templateScope\": \"local\",\n        \"className\": \"\",\n        \"x\": 140,\n        \"y\": 1420,\n        \"wires\": [\n            [\n                \"36b3b36c399ac7db\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f304680180a23479\",\n        \"type\": \"ui_toast\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"position\": \"dialog\",\n        \"displayTime\": \"3\",\n        \"highlight\": \"\",\n        \"sendall\": true,\n        \"outputs\": 1,\n        \"ok\": \"OK\",\n        \"cancel\": \"\",\n        \"raw\": true,\n        \"className\": \"\",\n        \"topic\": \"\",\n        \"name\": \"Info\",\n        \"x\": 630,\n        \"y\": 120,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"955f1e6794f368e2\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Scan\",\n        \"group\": \"7aaf184330605300\",\n        \"order\": 1,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Files&amp;Cloud</h1> <p><strong>Current Status</strong></p> <p style=\\\"margin-left: 20px;\\\"><u>--READY--&nbsp;</u>- everything is okay and ready to go :)</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-preparing</u> - before starting the routine some time might pass depending on the number of photos</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-stopping</u> - manually ending the routine by pressing the stop button</p> <p style=\\\"margin-left: 20px;\\\"><u>Routine-Photo X/Y</u> - Showing the progress of the routine</p> <p style=\\\"margin-left: 20px;\\\"><u>No Camera Found</u> - please check the camera ribbon cable</p> <p style=\\\"margin-left: 20px;\\\"><u>Error: XXX</u> - Please contact <a href=\\\"mailto:info@openscan.eu\\\">info@openscan.eu</a> or post an issue on <a href=\\\"https://github.com/OpenScanEu/OpenScan2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">Github.com</a></p> <p><strong>Projectname</strong></p> <p style=\\\"margin-left: 20px;\\\">Each photo set will be saved using the following pattern &nbsp;YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified &apos;default&apos; will be used.</p> <p><strong>Rotor</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the rotor by increments of 5&deg;. Please make sure to start the routine with the camera in the horizontal position.</p> <p><strong>Turntable</strong></p> <p style=\\\"margin-left: 20px;\\\">Moving the turntable by increments of 15&deg;.</p> <p><strong>Ringlight</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.</p> <p><strong>Photos</strong></p> <p style=\\\"margin-left: 20px;\\\">Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. <u><strong>If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!</strong></u></p> <p><strong>Shutter</strong></p> <p style=\\\"margin-left: 20px;\\\">Again: <strong><u>Less is more</u></strong>! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:</p> <p style=\\\"margin-left: 20px;\\\">- no polarizer: 5-20ms</p> <p style=\\\"margin-left: 20px;\\\">- mostly white object, &nbsp;with polarizer + one ringlight: 50-200ms</p> <p><strong>Crop X/Y</strong></p> <p style=\\\"margin-left: 20px;\\\">Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!</p> <p><strong>Start/Stop</strong></p> <p style=\\\"margin-left: 20px;\\\">Use the buttons to start/stop the routine</p> <p><strong>Reboot/Shutdown</strong></p> <p style=\\\"margin-left: 20px;\\\">In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!</p> <p><strong>MF - Manual Focus</strong></p> <p style=\\\"margin-left: 20px;\\\">By default, the switch is &apos;off&apos;, which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object).</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 470,\n        \"y\": 200,\n        \"wires\": [\n            [\n                \"f304680180a23479\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"dadf823225aa34c4\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadB\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\nif(data === '1' || data === 'True' || data === 'true'){\\n    data = true;\\n}\\nelse{\\n    data = false;\\n}\\nmsg.payload = data;\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 1620,\n        \"wires\": [\n            [\n                \"60f6ee795446200d\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"60f6ee795446200d\",\n        \"type\": \"ui_switch\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"\",\n        \"label\": \"Turntable Mode\",\n        \"tooltip\": \"\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 26,\n        \"width\": 6,\n        \"height\": 1,\n        \"passthru\": true,\n        \"decouple\": \"false\",\n        \"topic\": \"\",\n        \"topicType\": \"str\",\n        \"style\": \"\",\n        \"onvalue\": \"true\",\n        \"onvalueType\": \"bool\",\n        \"onicon\": \"\",\n        \"oncolor\": \"\",\n        \"offvalue\": \"false\",\n        \"offvalueType\": \"bool\",\n        \"officon\": \"\",\n        \"offcolor\": \"\",\n        \"animate\": false,\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 1620,\n        \"wires\": [\n            [\n                \"227d89d897a8bdf5\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"227d89d897a8bdf5\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'turntable_mode'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 470,\n        \"y\": 1620,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"8b317478e9d905b8\",\n        \"type\": \"ui_button\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Other Settings\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 24,\n        \"width\": 2,\n        \"height\": 1,\n        \"passthru\": false,\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"color\": \"\",\n        \"bgcolor\": \"transparent\",\n        \"className\": \"\",\n        \"icon\": \"fa-question-circle\",\n        \"payload\": \"<h1 style=\\\"text-align: left;\\\">Network Settings</h1> <p><strong>Hostname</strong></p> <p style=\\\"margin-left: 20px;\\\">The device can be accessed through any browser in the same network. Therefore, you can either enter the device&apos;s IP address or the given hostname. The standard name is &apos;openscan&apos; but it is highly recommended to change the name, when using multiple devices (e.g. &apos;openscan1&apos;, &apos;openscan2&apos; ...)</p> <p><strong>Select Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: <a href=\\\"https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">ISO 3166 Country Code on Wikipedia</a></p> <p><strong>Search Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">You can manually refresh the list of available networks by pressing this button.</p> <p><strong>Reset Wifi</strong></p> <p style=\\\"margin-left: 20px;\\\">Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see <a href=\\\"https://www.glennklockwood.com/sysadmin-howtos/rpi-headless-boot.html\\\" rel=\\\"noopener noreferrer\\\" target=\\\"_blank\\\">glennklockwood.com</a> for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)</p>\",\n        \"payloadType\": \"str\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"x\": 280,\n        \"y\": 80,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"814eaca62debe694\",\n        \"type\": \"ui_text\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 23,\n        \"width\": 4,\n        \"height\": 1,\n        \"name\": \"\",\n        \"label\": \"\",\n        \"format\": \"OTHER SETTINGS\",\n        \"layout\": \"col-center\",\n        \"className\": \"\",\n        \"x\": 170,\n        \"y\": 1580,\n        \"wires\": []\n    },\n    {\n        \"id\": \"9df2481a03f24d0a\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"loadS\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\ndata = fs.readFileSync(filepath+file, 'utf8');\\n\\nmsg.payload = String(data.replace(/(\\\\r\\\\n|\\\\n|\\\\r)/gm,\\\"\\\"));\\nreturn msg\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 170,\n        \"y\": 1660,\n        \"wires\": [\n            [\n                \"ecba1ecce99e3968\"\n            ]\n        ]\n    },\n    {\n        \"id\": \"f1de798f2a68e76e\",\n        \"type\": \"function\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"write\",\n        \"func\": \"var file = 'update_type'\\nlet fs = global.get('fs');\\nvar filepath = '/home/pi/OpenScan/settings/';\\nconst content = String(msg.payload)\\nfs.writeFile(filepath + file, content, err => {\\n  if (err) {\\n    return\\n  }\\n  });\",\n        \"outputs\": 1,\n        \"noerr\": 0,\n        \"initialize\": \"\",\n        \"finalize\": \"\",\n        \"libs\": [],\n        \"x\": 470,\n        \"y\": 1660,\n        \"wires\": [\n            []\n        ]\n    },\n    {\n        \"id\": \"ecba1ecce99e3968\",\n        \"type\": \"ui_dropdown\",\n        \"z\": \"017bd4e4a428bee5\",\n        \"name\": \"Update_select\",\n        \"label\": \"\",\n        \"tooltip\": \"\",\n        \"place\": \"Select version\",\n        \"group\": \"3b4bd36726be16d5\",\n        \"order\": 19,\n        \"width\": 0,\n        \"height\": 0,\n        \"passthru\": false,\n        \"multiple\": false,\n        \"options\": [\n            {\n                \"label\": \"Mini_simplified\",\n                \"value\": \"mini\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Advanced\",\n                \"value\": \"main\",\n                \"type\": \"str\"\n            },\n            {\n                \"label\": \"Beta\",\n                \"value\": \"beta\",\n                \"type\": \"str\"\n            }\n        ],\n        \"payload\": \"\",\n        \"topic\": \"topic\",\n        \"topicType\": \"msg\",\n        \"className\": \"\",\n        \"x\": 320,\n        \"y\": 1660,\n        \"wires\": [\n            [\n                \"f1de798f2a68e76e\"\n            ]\n        ]\n    }\n]"
  },
  {
    "path": "update/mini/settings.js",
    "content": "/**\n * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT\n *\n * It can contain any valid JavaScript code that will get run when Node-RED\n * is started.\n *\n * Lines that start with // are commented out.\n * Each entry should be separated from the entries above and below by a comma ','\n *\n * For more information about individual settings, refer to the documentation:\n *    https://nodered.org/docs/user-guide/runtime/configuration\n *\n * The settings are split into the following sections:\n *  - Flow File and User Directory Settings\n *  - Security\n *  - Server Settings\n *  - Runtime Settings\n *  - Editor Settings\n *  - Node Settings\n *\n **/\n\nmodule.exports = {\n\n/*******************************************************************************\n * Flow File and User Directory Settings\n *  - flowFile\n *  - credentialSecret\n *  - flowFilePretty\n *  - userDir\n *  - nodesDir\n ******************************************************************************/\n\n    /** The file containing the flows. If not set, defaults to flows_<hostname>.json **/\n    flowFile: \"flows.json\",\n\n    /** By default, credentials are encrypted in storage using a generated key. To\n     * specify your own secret, set the following property.\n     * If you want to disable encryption of credentials, set this property to false.\n     * Note: once you set this property, do not change it - doing so will prevent\n     * node-red from being able to decrypt your existing credentials and they will be\n     * lost.\n     */\n    credentialSecret: false,\n\n    /** By default, the flow JSON will be formatted over multiple lines making\n     * it easier to compare changes when using version control.\n     * To disable pretty-printing of the JSON set the following property to false.\n     */\n    flowFilePretty: true,\n\n    /** By default, all user data is stored in a directory called `.node-red` under\n     * the user's home directory. To use a different location, the following\n     * property can be used\n     */\n    //userDir: '/home/nol/.node-red/',\n\tuserDir: '/home/pi/OpenScan/settings/.node-red/',\n    /** Node-RED scans the `nodes` directory in the userDir to find local node files.\n     * The following property can be used to specify an additional directory to scan.\n     */\n    //nodesDir: '/home/nol/.node-red/nodes',\n\n/*******************************************************************************\n * Security\n *  - adminAuth\n *  - https\n *  - httpsRefreshInterval\n *  - requireHttps\n *  - httpNodeAuth\n *  - httpStaticAuth\n ******************************************************************************/\n\n    /** To password protect the Node-RED editor and admin API, the following\n     * property can be used. See http://nodered.org/docs/security.html for details.\n     */\n    //adminAuth: {\n    //    type: \"credentials\",\n    //    users: [{\n    //        username: \"admin\",\n    //        password: \"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\",\n    //        permissions: \"*\"\n    //    }]\n    //},\n    \n    /** The following property can be used to enable HTTPS\n     * This property can be either an object, containing both a (private) key\n     * and a (public) certificate, or a function that returns such an object.\n     * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener\n     * for details of its contents.\n     */\n\n    /** Option 1: static object */\n    //https: {\n    //  key: require(\"fs\").readFileSync('privkey.pem'),\n    //  cert: require(\"fs\").readFileSync('cert.pem')\n    //},\n\n    /** Option 2: function that returns the HTTP configuration object */\n    // https: function() {\n    //     // This function should return the options object, or a Promise\n    //     // that resolves to the options object\n    //     return {\n    //         key: require(\"fs\").readFileSync('privkey.pem'),\n    //         cert: require(\"fs\").readFileSync('cert.pem')\n    //     }\n    // },\n\n    /** If the `https` setting is a function, the following setting can be used\n     * to set how often, in hours, the function will be called. That can be used\n     * to refresh any certificates.\n     */\n    //httpsRefreshInterval : 12,\n\n    /** The following property can be used to cause insecure HTTP connections to\n     * be redirected to HTTPS.\n     */\n    //requireHttps: true,\n\n    /** To password protect the node-defined HTTP endpoints (httpNodeRoot),\n     * including node-red-dashboard, or the static content (httpStatic), the\n     * following properties can be used.\n     * The `pass` field is a bcrypt hash of the password.\n     * See http://nodered.org/docs/security.html#generating-the-password-hash\n     */\n    //httpNodeAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n    //httpStaticAuth: {user:\"user\",pass:\"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.\"},\n\n/*******************************************************************************\n * Server Settings\n *  - uiPort\n *  - uiHost\n *  - apiMaxLength\n *  - httpServerOptions\n *  - httpAdminRoot\n *  - httpAdminMiddleware\n *  - httpNodeRoot\n *  - httpNodeCors\n *  - httpNodeMiddleware\n *  - httpStatic\n ******************************************************************************/\n\n    /** the tcp port that the Node-RED web server is listening on */\n//    uiPort: process.env.PORT || 1880,\nuiPort: process.env.PORT || 80,\n    /** By default, the Node-RED UI accepts connections on all IPv4 interfaces.\n     * To listen on all IPv6 addresses, set uiHost to \"::\",\n     * The following property can be used to listen on a specific interface. For\n     * example, the following would only allow connections from the local machine.\n     */\n    //uiHost: \"127.0.0.1\",\n\n    /** The maximum size of HTTP request that will be accepted by the runtime api.\n     * Default: 5mb\n     */\n    //apiMaxLength: '5mb',\n\n    /** The following property can be used to pass custom options to the Express.js\n     * server used by Node-RED. For a full list of available options, refer\n     * to http://expressjs.com/en/api.html#app.settings.table\n     */\n    //httpServerOptions: { },\n\n    /** By default, the Node-RED UI is available at http://localhost:1880/\n     * The following property can be used to specify a different root path.\n     * If set to false, this is disabled.\n     */\n    //httpAdminRoot: '/admin',\nhttpAdminRoot: '/editor',\n    /** The following property can be used to add a custom middleware function\n     * in front of all admin http routes. For example, to set custom http\n     * headers. It can be a single function or an array of middleware functions.\n     */\n    // httpAdminMiddleware: function(req,res,next) {\n    //    // Set the X-Frame-Options header to limit where the editor\n    //    // can be embedded\n    //    //res.set('X-Frame-Options', 'sameorigin');\n    //    next();\n    // },\n\n\n    /** Some nodes, such as HTTP In, can be used to listen for incoming http requests.\n     * By default, these are served relative to '/'. The following property\n     * can be used to specifiy a different root path. If set to false, this is\n     * disabled.\n     */\n    //httpNodeRoot: '/red-nodes',\n\n    /** The following property can be used to configure cross-origin resource sharing\n     * in the HTTP nodes.\n     * See https://github.com/troygoode/node-cors#configuration-options for\n     * details on its contents. The following is a basic permissive set of options:\n     */\n    //httpNodeCors: {\n    //    origin: \"*\",\n    //    methods: \"GET,PUT,POST,DELETE\"\n    //},\n\n    /** If you need to set an http proxy please set an environment variable\n     * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.\n     * For example - http_proxy=http://myproxy.com:8080\n     * (Setting it here will have no effect)\n     * You may also specify no_proxy (or NO_PROXY) to supply a comma separated\n     * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk\n     */\n\n    /** The following property can be used to add a custom middleware function\n     * in front of all http in nodes. This allows custom authentication to be\n     * applied to all http in nodes, or any other sort of common request processing.\n     * It can be a single function or an array of middleware functions.\n     */\n    //httpNodeMiddleware: function(req,res,next) {\n    //    // Handle/reject the request, or pass it on to the http in node by calling next();\n    //    // Optionally skip our rawBodyParser by setting this to true;\n    //    //req.skipRawBodyParser = true;\n    //    next();\n    //},\n\n    /** When httpAdminRoot is used to move the UI to a different root path, the\n     * following property can be used to identify a directory of static content\n     * that should be served at http://localhost:1880/.\n     */\n    //httpStatic: '/home/nol/node-red-static/',\nhttpStatic: '/home/pi/OpenScan/',\n/*******************************************************************************\n * Runtime Settings\n *  - lang\n *  - logging\n *  - contextStorage\n *  - exportGlobalContextKeys\n *  - externalModules\n ******************************************************************************/\n\n     /** Uncomment the following to run node-red in your preferred language.\n      * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko\n      * Some languages are more complete than others.\n      */\n     // lang: \"de\",\n\n     /** Configure the logging output */\n     logging: {\n         /** Only console logging is currently supported */\n         console: {\n             /** Level of logging to be recorded. Options are:\n              * fatal - only those errors which make the application unusable should be recorded\n              * error - record errors which are deemed fatal for a particular request + fatal errors\n              * warn - record problems which are non fatal + errors + fatal errors\n              * info - record information about the general running of the application + warn + error + fatal errors\n              * debug - record information which is more verbose than info + info + warn + error + fatal errors\n              * trace - record very detailed logging + debug + info + warn + error + fatal errors\n              * off - turn off all logging (doesn't affect metrics or audit)\n              */\n             level: \"info\",\n             /** Whether or not to include metric events in the log output */\n             metrics: false,\n             /** Whether or not to include audit events in the log output */\n             audit: false\n         }\n     },\n\n     /** Context Storage\n      * The following property can be used to enable context storage. The configuration\n      * provided here will enable file-based context that flushes to disk every 30 seconds.\n      * Refer to the documentation for further options: https://nodered.org/docs/api/context/\n      */\n     //contextStorage: {\n     //    default: {\n     //        module:\"localfilesystem\"\n     //    },\n     //},\n\n     /** `global.keys()` returns a list of all properties set in global context.\n      * This allows them to be displayed in the Context Sidebar within the editor.\n      * In some circumstances it is not desirable to expose them to the editor. The\n      * following property can be used to hide any property set in `functionGlobalContext`\n      * from being list by `global.keys()`.\n      * By default, the property is set to false to avoid accidental exposure of\n      * their values. Setting this to true will cause the keys to be listed.\n      */\n     exportGlobalContextKeys: false,\n\n     /** Configure how the runtime will handle external npm modules.\n      * This covers:\n      *  - whether the editor will allow new node modules to be installed\n      *  - whether nodes, such as the Function node are allowed to have their\n      * own dynamically configured dependencies.\n      * The allow/denyList options can be used to limit what modules the runtime\n      * will install/load. It can use '*' as a wildcard that matches anything.\n      */\n     externalModules: {\n         // autoInstall: false,   /** Whether the runtime will attempt to automatically install missing modules */\n         // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */\n         // palette: {              /** Configuration for the Palette Manager */\n         //     allowInstall: true, /** Enable the Palette Manager in the editor */\n         //     allowUpload: true,  /** Allow module tgz files to be uploaded and installed */\n         //     allowList: [],\n         //     denyList: []\n         // },\n         // modules: {              /** Configuration for node-specified modules */\n         //     allowInstall: true,\n         //     allowList: [],\n         //     denyList: []\n         // }\n     },\n\n\n/*******************************************************************************\n * Editor Settings\n *  - disableEditor\n *  - editorTheme\n ******************************************************************************/\n\n    /** The following property can be used to disable the editor. The admin API\n     * is not affected by this option. To disable both the editor and the admin\n     * API, use either the httpRoot or httpAdminRoot properties\n     */\n    //disableEditor: false,\n\n    /** Customising the editor\n     * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes\n     * for all available options.\n     */\n    editorTheme: {\n        /** The following property can be used to set a custom theme for the editor.\n         * See https://github.com/node-red-contrib-themes/theme-collection for\n         * a collection of themes to chose from.\n         */\n        //theme: \"\",\n        palette: {\n            /** The following property can be used to order the categories in the editor\n             * palette. If a node's category is not in the list, the category will get\n             * added to the end of the palette.\n             * If not set, the following default order is used:\n             */\n            //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],\n        },\n        projects: {\n            /** To enable the Projects feature, set this value to true */\n            enabled: false,\n            workflow: {\n                /** Set the default projects workflow mode.\n                 *  - manual - you must manually commit changes\n                 *  - auto - changes are automatically committed\n                 * This can be overridden per-user from the 'Git config'\n                 * section of 'User Settings' within the editor\n                 */\n                mode: \"manual\"\n            }\n        },\n        codeEditor: {\n            /** Select the text editor component used by the editor.\n             * Defaults to \"ace\", but can be set to \"ace\" or \"monaco\"\n             */\n            lib: \"ace\",\n            options: {\n                /** The follow options only apply if the editor is set to \"monaco\"\n                 *\n                 * theme - must match the file name of a theme in\n                 * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme\n                 * e.g. \"tomorrow-night\", \"upstream-sunburst\", \"github\", \"my-theme\"\n                 */\n                theme: \"vs\",\n                /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.\n                 * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html\n                 */\n                //fontSize: 14,\n                //fontFamily: \"Cascadia Code, Fira Code, Consolas, 'Courier New', monospace\",\n                //fontLigatures: true,\n            }\n        }\n    },\n\n/*******************************************************************************\n * Node Settings\n *  - fileWorkingDirectory\n *  - functionGlobalContext\n *  - functionExternalModules\n *  - nodeMessageBufferMaxLength\n *  - ui (for use with Node-RED Dashboard)\n *  - debugUseColors\n *  - debugMaxLength\n *  - execMaxBufferSize\n *  - httpRequestTimeout\n *  - mqttReconnectTime\n *  - serialReconnectTime\n *  - socketReconnectTime\n *  - socketTimeout\n *  - tcpMsgQueueSize\n *  - inboundWebSocketTimeout\n *  - tlsConfigDisableLocalFiles\n *  - webSocketNodeVerifyClient\n ******************************************************************************/\n\n    /** The working directory to handle relative file paths from within the File nodes\n     * defaults to the working directory of the Node-RED process.\n     */\n    //fileWorkingDirectory: \"\",\n\n    /** Allow the Function node to load additional npm modules directly */\n    functionExternalModules: true,\n\n    /** The following property can be used to set predefined values in Global Context.\n     * This allows extra node modules to be made available with in Function node.\n     * For example, the following:\n     *    functionGlobalContext: { os:require('os') }\n     * will allow the `os` module to be accessed in a Function node using:\n     *    global.get(\"os\")\n     */\n    functionGlobalContext: {\n        os:require('os'),\n        path:require('path'),\n        fs:require('fs'),\n\n},\n\n    /** The maximum number of messages nodes will buffer internally as part of their\n     * operation. This applies across a range of nodes that operate on message sequences.\n     * defaults to no limit. A value of 0 also means no limit is applied.\n     */\n    //nodeMessageBufferMaxLength: 0,\n\n    /** If you installed the optional node-red-dashboard you can set it's path\n     * relative to httpNodeRoot\n     * Other optional properties include\n     *  readOnly:{boolean},\n     *  middleware:{function or array}, (req,res,next) - http middleware\n     *  ioMiddleware:{function or array}, (socket,next) - socket.io middleware\n     */\n    //ui: { path: \"ui\" },\nui: { path: \"\" },\n    /** Colourise the console output of the debug node */\n    //debugUseColors: true,\n\n    /** The maximum length, in characters, of any message sent to the debug sidebar tab */\n    debugMaxLength: 1000,\n\n    /** Maximum buffer size for the exec node. Defaults to 10Mb */\n    //execMaxBufferSize: 10000000,\n\n    /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */\n    //httpRequestTimeout: 120000,\n\n    /** Retry time in milliseconds for MQTT connections */\n    mqttReconnectTime: 15000,\n\n    /** Retry time in milliseconds for Serial port connections */\n    serialReconnectTime: 15000,\n\n    /** Retry time in milliseconds for TCP socket connections */\n    //socketReconnectTime: 10000,\n\n    /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */\n    //socketTimeout: 120000,\n\n    /** Maximum number of messages to wait in queue while attempting to connect to TCP socket\n     * defaults to 1000\n     */\n    //tcpMsgQueueSize: 2000,\n\n    /** Timeout in milliseconds for inbound WebSocket connections that do not\n     * match any configured node. Defaults to 5000\n     */\n    //inboundWebSocketTimeout: 5000,\n\n    /** To disable the option for using local files for storing keys and\n     * certificates in the TLS configuration node, set this to true.\n     */\n    //tlsConfigDisableLocalFiles: true,\n\n    /** The following property can be used to verify websocket connection attempts.\n     * This allows, for example, the HTTP request headers to be checked to ensure\n     * they include valid authentication information.\n     */\n    //webSocketNodeVerifyClient: function(info) {\n    //    /** 'info' has three properties:\n    //    *   - origin : the value in the Origin header\n    //    *   - req : the HTTP request\n    //    *   - secure : true if req.connection.authorized or req.connection.encrypted is set\n    //    *\n    //    * The function should return true if the connection should be accepted, false otherwise.\n    //    *\n    //    * Alternatively, if this function is defined to accept a second argument, callback,\n    //    * it can be used to verify the client asynchronously.\n    //    * The callback takes three arguments:\n    //    *   - result : boolean, whether to accept the connection or not\n    //    *   - code : if result is false, the HTTP error status to return\n    //    *   - reason: if result is false, the HTTP reason string to return\n    //    */\n    //},\n}\n"
  },
  {
    "path": "update/update.json",
    "content": "{\n    \"mini\": {\n        \"1\": {\n            \"src\": \"mini/fla.py\",\n            \"dst\": \"/home/pi/OpenScan/files/fla.py\",\n            \"filesize\": 3910\n        },\n        \"2\": {\n            \"src\": \"mini/Arducam.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/Arducam.py\",\n            \"filesize\": 6154\n        },\n        \"3\": {\n            \"src\": \"mini/OpenScan.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/OpenScan.py\",\n            \"filesize\": 5864\n        },\n        \"4\": {\n            \"src\": \"mini/config.txt\",\n            \"dst\": \"/boot/config.txt\",\n            \"filesize\": 2196\n        },\n        \"5\": {\n            \"src\": \"mini/flows.json\",\n            \"dst\": \"/home/pi/OpenScan/settings/.node-red/flows.json\",\n            \"filesize\": 178895\n        },\n        \"6\": {\n            \"src\": \"mini/settings.js\",\n            \"dst\": \"/root/.node-red/settings.js\",\n            \"filesize\": 20321\n        },\n        \"7\": {\n            \"src\": \"files/logo2.jpg\",\n            \"dst\": \"/home/pi/OpenScan/files/logo.jpg\",\n            \"filesize\": 582519\n        }\n    },\n    \"betaArdu\": {\n        \"1\": {\n            \"src\": \"betaArdu/fla.py\",\n            \"dst\": \"/home/pi/OpenScan/files/fla.py\",\n            \"filesize\": 12089\n        },\n        \"2\": {\n            \"src\": \"betaArdu/OpenScan.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/OpenScan.py\",\n            \"filesize\": 10253\n        },\n        \"3\": {\n            \"src\": \"betaArdu/config.txt\",\n            \"dst\": \"/boot/config.txt\",\n            \"filesize\": 2185\n        },\n        \"4\": {\n            \"src\": \"betaArdu/flows.json\",\n            \"dst\": \"/home/pi/OpenScan/settings/.node-red/flows.json\",\n            \"filesize\": 315147\n        },\n        \"5\": {\n            \"src\": \"betaArdu/settings.js\",\n            \"dst\": \"/root/.node-red/settings.js\",\n            \"filesize\": 21199\n        }\n    },\n    \"main\": {\n        \"1\": {\n            \"src\": \"main/fla.py\",\n            \"dst\": \"/home/pi/OpenScan/files/fla.py\",\n            \"filesize\": 5445\n        },\n        \"2\": {\n            \"src\": \"main/Arducam.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/Arducam.py\",\n            \"filesize\": 6154\n        },\n        \"3\": {\n            \"src\": \"main/OpenScan.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/OpenScan.py\",\n            \"filesize\": 6161\n        },\n        \"4\": {\n            \"src\": \"main/config.txt\",\n            \"dst\": \"/boot/config.txt\",\n            \"filesize\": 2179\n        },\n        \"5\": {\n            \"src\": \"main/flows.json\",\n            \"dst\": \"/home/pi/OpenScan/settings/.node-red/flows.json\",\n            \"filesize\": 360661\n        },\n        \"6\": {\n            \"src\": \"main/settings.js\",\n            \"dst\": \"/root/.node-red/settings.js\",\n            \"filesize\": 20321\n        },\n        \"7\": {\n            \"src\": \"files/logo.jpg\",\n            \"dst\": \"/home/pi/OpenScan/files/logo.jpg\",\n            \"filesize\": 582519\n        }\n    },\n    \"beta\": {\n        \"1\": {\n            \"src\": \"beta/fla.py\",\n            \"dst\": \"/home/pi/OpenScan/files/fla.py\",\n            \"filesize\": 5366\n        },\n        \"2\": {\n            \"src\": \"beta/Arducam.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/Arducam.py\",\n            \"filesize\": 6154\n        },\n        \"3\": {\n            \"src\": \"beta/OpenScan.py\",\n            \"dst\": \"/usr/lib/python3/dist-packages/OpenScan.py\",\n            \"filesize\": 6443\n        },\n        \"4\": {\n            \"src\": \"beta/config.txt\",\n            \"dst\": \"/boot/config.txt\",\n            \"filesize\": 2179\n        },\n        \"5\": {\n            \"src\": \"beta/flows.json\",\n            \"dst\": \"/home/pi/OpenScan/settings/.node-red/flows.json\",\n            \"filesize\": 354569\n        },\n        \"6\": {\n            \"src\": \"beta/settings.js\",\n            \"dst\": \"/root/.node-red/settings.js\",\n            \"filesize\": 20321\n        },\n        \"7\": {\n            \"src\": \"files/logo.jpg\",\n            \"dst\": \"/home/pi/OpenScan/files/logo.jpg\",\n            \"filesize\": 582519\n        }\n    }\n}"
  }
]