Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. BananaJoe

    NEWS

    • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?

    • Monatsrückblick – September 2025

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

    • Profile
    • Following 27
    • Followers 13
    • Topics 65
    • Posts 5330
    • Best 886
    • Groups 3

    BananaJoe

    @BananaJoe

    Most Active

    1066
    Reputation
    390
    Profile views
    5330
    Posts
    13
    Followers
    27
    Following
    Joined Last Online
    Website znil.net Location Achim bei Bremen Age 52

    BananaJoe Follow
    Pro Starter Most Active

    Best posts made by BananaJoe

    • RE: Bestückung Netzwerkschrank (Neubau)

      @chillkroete1206 also Patchpanel mit 48 Ports .. wird schon ein Mörder Teil wenn da dann 48 (44) Cat. 7 Kabel von hinten dran kommen. Überlege dir die Kabelführung, das wird ein ordentlicher Strang. Den du nie wieder bewegen kannst. Also so legen das da nichts verdeckt wird wo du noch mal ran müsstest oder du noch mal ein Loch durchbohren würdest.

      Ich habe es zu Hause auf 2 x 24 Patchpanel verteilt + Kabelführung. Dazu ist nachträglich noch ein 16er Patchfeld gekommen (Datenkabel aus Garage nachverlegt usw.) Sind bei mir im Reihenhaus 56 aktive Anschlüsse.

      Thema Switch: Wir sind zu 4 in einem Reihenhaus, was braucht man da schon an Leitungen ...
      mhh, der 24 + 4 Port Switch ist voll, der 8 + 2 Port PoE ist fast voll, der 16 Port Netgear ist auch voll ... es sammelt sich (5 VoIP Telefone, 5 AccessPoints, 4 FireTV-Sticks (alle mit LAN Adapter) + 2 Fernseher, Playstation, 4 Laptops, Computer, Server mit 2fach Trunk, 2 x selbst-bau ESXi, 2 x Raspberry Pi (1 x ESXi, 1x KVM), 1 x Backups-NAS mit Trunk, 12 Kameras (auch wenn nicht alle in Betrieb sind weil sinnfrei wie die Kamera über dem Brotbackautomat) ...

      Die Patchfelder im Schrank wollen auch alle geführt werden (ich nutze inzwischen flache 5x1mm Patchkabel, das kurze Stück geht das). Ich würde Platz für das Kabelrangieren einplanen mit einem HE drüber und drunter.
      Und auch eine Steckdosenleiste vorne. Ich habe auch eine hinten an die ich inzwischen quasi gar nicht mehr ran komme.
      Ich hab sogar seitlich ein Loch in den Schrank geschnitten (nach 8 Jahren nachträglich) um überhaupt hinten noch mal an was ranzukommen. In das Loch habe ich 2 x 120mm USB Lüfter gepackt (auf niedriger Drehzahl), seitdem sind meine lüfterlosen Switche auch nicht mehr heiß.
      Und meine Steckdosenleiste vorne ist inzwischen so voll das da schon 2 x 7 Port hängen. (Zu dem Server + ESXi`s + Raspis kommen noch Netzteil für USB Lüfter, Lampe im Schrank, Netzteil Klingelpaul und ESP8266 zur Auswertung vom Klingelpaul, 1x Fritzbox alt und 1 x neu und die dritte kommt mit dem Glasfaseranschluß.

      Sei also sehr sehr großzügig. Bei allen. Und du kannst dir vorstellen was das für ein riesen Kabelsalat wird (und ist), also auch bitte schon gleich alles Beschriften, jedes Kabel, Patchkabel, Stromkabel, Koaxialkabel, immer an beiden Enden! Es gibt da schöne Klebeetiketten für diese Zwecke. Ich habe da heute noch Patchkabel drin von denen ich nicht mehr weis wo diese Enden (muss irgendwas aus dem gleichen Raum sein, aber keine 10 Jahre später vergessen wofür). Vergiss das schöne Bündeln von Stromanschluss- oder Datenkabeln, das wird schneller auseinander gerissen als du denkst, habe lieber Platz das du so ein Kabel auch wieder rausbekommst. Spätestens wenn du das erste mal ein Kabel das nicht mehr gebraucht wird nicht gleich wieder rausziehst hast du später verloren (Stichwort "Vergessen wofür" oder "Was war jetzt AEG?". Und du wird welche rausziehen.

      Ach ja, und schwarze Kunststoff-Quader, hatte ich schon von schwarzen Kunststoffquadern erzählt? Werden auch Netzteile genannt. Erst hast du einen Switch mit Kaltgeräteanschluss, der wird getauscht und der nächste kommt mit einem schwarzen Kunststoffquader. Habe ich erwähnt das du den gleich und sofort beschriften solltest wofür der ist? Das dann der Zeitpunkt ist wo du ein Kaltgerätekabel wieder rauspulen, ein Euro-Stecker-Kabel neu verlegen und irgendwo diesen Quader lassen musst?

      Hatte ich eigentlich erwähnt das du extra Platz einplanen solltest? Viel extra Platz. Ganz viel extra Platz. Für Kabelführung, die Möglichkeit ein Kabel auch wieder raus zu ziehen? Für schwarze Quader? Das dann doch neue extra NAS (nur echt mit extra Quader!) .. und das PVR System und das nur mal ausprobieren Ding ...

      Hatte ich schon USV erwähnt? Ein großer schwarzer Kunststoffblock? An den du die Steckdosenleisten hoffentlich alle anschließt? Und der erste Test, prima, hält 30 Minuten das Ding. Öhm, 2 Jahre später beim Stromausfall merkst du das plötzlich nur noch 3 Minuten sind wegen der vielen schwarzen Stromkabel und Quader ... hatte ich erwähnt das du Reserveplatz brauchst, z.B. für die 2. USV?

      Ok, ok, ich hab mich etwas in Rage geschrieben 🙂 Ich hoffe die Botschaft kommt an.

      Bei den Datendosen werden es die ausgewählten Dosen mit LSA-Plus tun. Die baust du nur einmal ein und fast die nie wieder an. Tu dir einen gefallen und nimm tiefe Einbaudosen. Inzwischen gibt es sogar tiefe Einbaudosen mit Tasche nach unten.

      Wenn die Neugier plagt: Ich wollte damals in der "Waschküche" einen 12HE Wandschrank aufhängen, so 400mm tief. Der wurde von meiner Frau nicht genehmigt "weil der zu viel Platz wegnimmt".
      Ich hatte in der alten Wohnung noch einen 40HE Datenschrank, 600mm breit, 800mm tief den ich schwarz gestrichen und als Vitrine genutzt hatte. Den habe ich mir in mein Kellerbüro gestellt:
      502477a5-1c53-4e1d-ad7f-fa83ba361bc0-Datenschrank1.jpg

      posted in Hardware
      BananaJoe
      BananaJoe
    • [Vorlage] Schimpfwortgenerator

      Changelog:

      • 04.10.2024 erste Version
      • 05.10.2024 Variante die 1x die Minute ein Schimpfwort in einen Datenpunkt schreibt ergänzt
      • 05.10.2024 Fehler im Skript für Datenpunkt behoben (aktualisiere statt steuere da Datenpunkt schreibgeschützt)
      • 21.10.2024 Bereinigte Schimpfwortliste (Danke an @JoJo58 )

      Ich bin über dieses Projekt hier gestolpert: https://github.com/NikolaiRadke/Schimpfolino/wiki

      Ich nutze bei uns im Smarthome einige Sprachausgaben über Alexa, z.B. "Waschmaschine ist fertig" oder "Mittagessen" usw.
      Diese wollte ich mit dem Schimpfwortgenerator etwas aufpolieren und habe mir deshalb eine JavaScript-Funktion für Blockly dafür gebaut:
      1d12dc55-68e7-4b99-83a3-9b70bef21194-image.png
      Die Wortlisten habe ich aus dem Projekt entnommen, ebenso das Funktionsprinzip der Ermittlung welches im dazu gehörigen Make-Magazin Artikel genau erklärt wird: https://www.heise.de/ratgeber/Roboter-im-Eigenbau-Teil-1-Ein-Schimpfwortgenerator-9857007.html?seite=all
      Export:

      <block xmlns="https://developers.google.com/blockly/xml" type="procedures_defcustomreturn" id="h!J2T(,rRS$uRe+#Tw8y" x="38" y="-337">
        <mutation statements="false"></mutation>
        <field name="NAME">Schimpfwortgenerator</field>
        <field name="SCRIPT">Ly8gRnJlaSBuYWNoIGh0dHBzOi8vZ2l0aHViLmNvbS9OaWtvbGFpUmFka2UvU2NoaW1wZm9saW5vL3dpa2kNCg0KLy8gVGVpbCAxOiBXb3J0bGlzdGVuIGRlZmluaWVyZW4NCnZhciBhX3dvcnRsaXN0ZTEgPSBbIkR1bXBmZSIsIlN0YXViaWdlIiwiTWllZmVuZGUiLCJTdGlua2VuZGUiLCJHYW1tbGlnZSIsIkhpbmtlbmRlIiwiV2luemlnZSIsIlBvcGVsaWdlIiwiTmFzc2UiLCJGdXJ6ZW5kZSIsIlJvc3RpZ2UiLCJIb2hsZSIsIlNpZmZpZ2UiLCJNaWVzZSIsIktydW1tZSIsIktsYXBwcmlnZSIsIlRyb2NrZW5lIiwiSGFhcmlnZSIsIlVyYWx0ZSIsIkdydW56ZW5kZSIsIlNjaHJlaWVuZGUiLCJNZWNrZXJuZGUiLCJOZXJ2ZW5kZSIsIlNhYmJlcm5kZSIsIlRyaWVmZW5kZSIsIk1vZHJpZ2UiLCJMdW1waWdlIiwiTGF1c2lnZSIsIlNpbm5sb3NlIiwiT2xsZSIsIlVubsO2dGlnZSIsIkRhbXBmZW5kZSIsIkxlZHJpZ2UiLCJFaW5hcm1pZ2UiLCJMZWVyZSIsIkzDpHN0aWdlIiwiSGV1bGVuZGUiLCJQaWNrZWxpZ2UiLCJGYXVsZSIsIlJhbnppZ2UiLCJUcsO8YmUiLCJEcmFsbGUiLCJCbGFua2UiLCJHaWVyaWdlIiwiVHJhbmlnZSIsIldhY2tlbG5kZSIsIlRvcmtlbG5kZSIsIlfDvHN0ZSIsIkZpc2NoaWdlIiwiQmVrbmFja3RlIiwiVmVya29ya3N0ZSIsIkhlaW1saWNoZSIsIkzDtmNocmlnZSIsIkJyb2NraWdlIiwiUGx1bXBlIiwiVGF0dHJpZ2UiLCJSYXR0ZXJuZGUiLCJTY2htdXR6aWdlIiwiTGlkZXJsaWNoZSIsIkTDtnNpZ2UiLCJQcm9sbGlnZSIsIkZpZXNlIiwiTXVmZmlnZSIsIk3DvGZmZWxuZGUiLCJQZWlubGljaGUiLCJOw7ZyZ2VsbmRlIiwiRmV0dGlnZSIsIlphaG5sb3NlIiwiRnJlY2hlIiwiU2Now6RiaWdlIiwiUGllZmlnZSIsIkd1bW1pZ2UiLCJMYWJicmlnZSIsIlBhdHppZ2UiLCJQZWx6aWdlIiwiUmV1ZGlnZSIsIlBla2lnZSIsIk3DvHJiZSIsIkhhcnppZ2UiLCJMYWhtZSIsIk1pY2tyaWdlIiwiQnLDpHNpZ2UiLCJab3R0ZWxpZ2UiLCJHZWxibGljaGUiLCJLbm9ycmlnZSIsIlNhbHppZ2UiLCJTY2hyaWxsZSIsIkR1c3NlbGlnZSIsIldpbmRpZ2UiLCJHcmF1c2lnZSIsIkdyw6Rzc2xpY2hlIiwiR3JvYmUiLCJTcGFja2lnZSIsIkthdXppZ2UiLCJGbGFjaHNpZ2UiLCJGcmFuc2lnZSIsIk1vdHppZ2UiLCJLYWhsZSIsIk5pZWRyaWdlIiwiS2VpZmVuZGUiLCJOaWNodGlnZSIsIkRyw7ZnZSIsIkZhZGUiLCJXZWluZW5kZSIsIkhpYmJlbGlnZSIsIlBsb2NraWdlIiwiQnJlbm5lbmRlIiwiRMO8cnJlIiwiS29jaGVuZGUiLCJLbmFyemVuZGUiLCJGYWx0aWdlIiwiU2NobGFtbWlnZSIsIkJyw7Zja2VsaWdlIiwiUmlzc2lnZSIsIlZlcmtlaW10ZSIsIkthbnRpZ2UiLCJHZWtsYXV0ZSIsIlF1aWVja2VuZGUiLCJGYXNlbG5kZSIsIkJlaXNzZW5kZSIsIkdlaMO2cm50ZSIsIlZlcmdlc3NlbmUiLCJCbGVpY2hlIiwiWndlaWNrZW5kZSIsIkZyb3N0aWdlIiwiTmFja2lnZSIsIkdydXNlbGlnZSIsIk1pbmRlcmUiLCJIYWdlcmUiLCJNYWdlcmUiLCJTY2h1cHBpZ2UiLCJCZWxlZ3RlIiwiU3TDpG5rZXJuZGUiLCJCw7ZzYXJ0aWdlIiwiUm9sbGVuZGUiLCJTY2hlY2tpZ2UiLCJSdWJiZWxuZGUiLCJTY2hpZWxlbmRlIiwiVHJhdHNjaGlnZSIsIkdyb3Rlc2tlIiwiQWJzdXJkZSIsIk1laGxpZ2UiLCJQbGF0dGUiLCJNw7xkZSIsIlRvdGFsZSIsIkJla2xvcHB0ZSIsIlNjaGF1cmlnZSIsIlRhdWJlIiwiQmV0w6R1YnRlIiwiQmVow6RtbWVydGUiLCJCZWxhbmdsb3NlIiwiQmVsZWlkaWd0ZSIsIkJldHJ1bmtlbmUiLCJCaXphcnJlIiwiRGlmZnVzZSJdOw0KdmFyIGFfd29ydGxpc3RlMiA9IFsiU3RhbXBmIiwiV2FiYmVsIiwiUHVwcyIsIlNjaG1hbHoiLCJTY2htaWVyIiwiSGFjayIsIlplbWVudCIsIlNwdWNrIiwiU3RhY2hlbCIsIktlbGxlciIsIkxhYmVyIiwiU3RvY2siLCJSdW56ZWwiLCJTY2hydW1wZiIsIkVrZWwiLCJTY2hub2RkZXIiLCJNYXRzY2giLCJXdXJtIiwiRWl0ZXIiLCJTcGVjayIsIk1pc3QiLCJLbG90eiIsIlfDvHJnIiwiTHVtcGVuIiwiU2NobGVpbSIsIld1cnN0IiwiRG9vZiIsIkJyYXQiLCJTY2h3YW1tIiwiS3JhdHoiLCJHcm90dGVuIiwiS3JpZWNoIiwiR2lmdCIsIlNjaGxhYmJlciIsIkfDtmJlbCIsIktsZWIiLCJTY2htYWRkZXIiLCJHcmluZCIsIkxhYmJlciIsIkx1ZnQiLCJNYXNzZW4iLCJTY2hpbW1lbCIsIk1pbmkiLCJPY2hzZW4iLCJQcm9ibGVtIiwiUXVhc3NlbCIsIlNjaG5hcHMiLCJTYWZ0IiwiRnJpZW1lbCIsIlphcHBlbCIsIlRyb3BmIiwiUGx1bnRzY2giLCJTdW1wZiIsIkhlY2tlbiIsIkdyYWIiLCJTY2h3aXR6IiwiU2NobmFyY2giLCJTY2hsZWljaCIsIlNjaGx1ZmYiLCJGbMO2dGVuIiwiSG9seiIsIktyZWlzY2giLCJEdWxsaSIsIkx1c2NoZW4iLCJHYW1tZWwiLCJBbHTDtmwiLCJSw7ZjaGVsIiwiR2xpYmJlciIsIkxhY2giLCJLcmFjaCIsIktuaWNrIiwiUXVldHNjaCIsIlF1YXRzY2giLCJRdWlldHNjaCIsIktuYXV0c2NoIiwiVMO8bXBlbCIsIlRlaWNoIiwiS25hdHRlciIsIlNhdWYiLCJQaXBpIiwiU3RydWxsZXIiLCJHcsOkdGVuIiwiTmFzZW4iLCJQZWNoIiwiTGVpZXIiLCJSZWllciIsIkJsw7ZkIiwiU2Nob3JmIiwiU2FiYmVsIiwiUXVlbmdlbCIsIkJhbmFuZW4iLCJVbnNpbm5zIiwiUGx1bnNjaCIsIkZydXN0IiwiTG90dGVyIiwiRnVtbWVsIiwiQmx1YmJlciIsIldvYmJlbCIsIlZvbGxiYXJ0IiwiTGFjayIsIktsbyIsIk1vZGVyIiwiS25pcnNjaCIsIlppdHRlciIsIkthbHQiLCJTY2hsw7xyZiIsIlNjaG5pZWYiLCJLbGVja2VyIiwiUnVtYmEiLCJTY2h3dXJiZWwiLCJTY2hyYWJiZWwiLCJTY2hsYXVjaCIsIlNjaHJ1bXBlbCIsIkjDvGhuZXIiLCJTY2hsYWNrZXIiLCJCcmFiYmVsIiwiS3JhbXBmIiwiUHLDvGdlbCIsIlJhcHBlbCIsIlp1cHBlbCIsIlBsdW5kZXIiLCJEb25uZXIiLCJSaWVzZW4iLCJCdXR0ZXIiLCJXaXNjaHdhc2NoIiwiUG9sdGVyIiwiVHJhbXBlbCIsIlNhdWVyIiwiSGFtcGVsIiwiQml0dGVyIiwiTWFzc2VsIiwiRmxpdHoiLCJXYXJtIiwiU2NobGluZyIsIlBsdW1wcyIsIlF1w6RsIiwiU3RyYW1wZWwiLCJTY2hsZWNrIiwiUmVjeWNsaW5nIiwiRWdhbCIsIkJsZWNoIiwiSG9ycm9yIiwiUnVtcGVsIiwiU2NobnVsbGVyIiwiU2NoZXJ6IiwiTmFja3QiLCJQYW1wZWwiLCJNb3Jhc3QiLCJGbGFjaCIsIkFuZ3N0IiwiU3BlaSIsIlB1bXBlbCIsIkF1c3NjaGxhZyIsIlF1YWxtIiwiUmFtYmF6YW1iYSIsIktsZWluIiwiU3BydWRlbCJdOw0KdmFyIGFfd29ydGxpc3RlMyA9IFsic3VwcGUiLCJzb2NrZSIsImJvbWJlIiwiYm91bGV0dGUiLCJzY2h3YXJ0ZSIsIndhcnplIiwiYmV1bGUiLCJwZXN0IiwicGZsYXVtZSIsInLDvGJlIiwiZ2VpZ2UiLCJyYXR0ZSIsImtyYW5raGVpdCIsInd1bmRlIiwib21hIiwic3R1bGxlIiwibGllc2UiLCJicnV0IiwiaGVubmUiLCJ6d2llYmVsIiwiYnVkZSIsImtpc3RlIiwiYnJhdXQiLCJsZXVjaHRlIiwia3LDtnRlIiwibnVzcyIsInNwaW5uZSIsImdydWJlIiwidG9pbGV0dGUiLCJrcmFrZSIsInBmw7x0emUiLCJiYWNrZSIsImJyYXRzY2hlIiwia2xhdHNjaGUiLCJudWRlbCIsImtub2xsZSIsInTDvHRlIiwibmFzZSIsIm1hZGUiLCJ0b25uZSIsImtyYW1wZSIsImLDvHJzdGUiLCJ3aW5kZWwiLCJzZW1tZWwiLCJoYXhlIiwiZ3LDpGZpbiIsInNjaGxldWRlciIsInppZXJkZSIsImtyw6RoZSIsImxhdHRlIiwibmlldGUiLCJyYXNzZWwiLCJhc3NlbCIsInRvcnRlIiwiZ2FsbGUiLCJsYXRzY2hlIiwic2NocnVsbGUiLCJrYW5vbmUiLCJibGFzZSIsInBlbGxlIiwidHJpbmUiLCJxdWVlbiIsInplY2tlIiwicHJhbGluZSIsIm1hZ3QiLCJwcmFjaHQiLCJmcml0dGUiLCJzb3NzZSIsImxhcnZlIiwibXVybWVsIiwiaGV4ZSIsInBhbXBlIiwic2lyZW5lIiwiZHLDvHNlIiwia2xldHRlIiwicGV0emUiLCJicnVtbWUiLCJnbGF0emUiLCJxdWFsbGUiLCJuYXR0ZXIiLCJrcmFsbGUiLCJ6aWVnZSIsImdyw7x0emUiLCJzw7xsemUiLCJudWxwZSIsIndhbXBlIiwiZnJpa2FkZWxsZSIsImZsdW5kZXIiLCJ0cnVsbGEiLCJ6aWNodGUiLCJ1c2NoaSIsImt1aCIsInBhcHBlIiwiaHVwZSIsInRyw7Z0ZSIsInNjaGFiZSIsImthbmFsbGllIiwic2NoYXJ0ZSIsInJpbGxlIiwiYW1zZWwiLCJhbGdlIiwibMO8Y2tlIiwiYnJlbXNlIiwibcO8Y2tlIiwiYsO8cnN0ZSIsIndhbm5lIiwicG9ja2UiLCJwbMO2cnJlIiwic2NoYWJyYWNrZSIsInd1cHBlIiwic2ljaGVsIiwidGFudGUiLCJyZXVzZSIsInJhdHNjaGUiLCJwYXVrZSIsImZsdXBwZSIsIm1hdHJvbmUiLCJodW1tZWwiLCJwYXJhZGUiLCJhdHRyYXBwZSIsImzDvGdlIiwiZmxvc3NlIiwiZnVuemVsIiwiZ3Vya2UiLCJwaWVwZSIsImfDtnJlIiwia29sYmVuIiwic2FtbWx1bmciLCJwcmltZWwiLCJvbW1lIiwibG90dGUiLCJ1bmtlIiwic3RyaXBwZSIsInNlaWZlIiwicGzDtnR6ZSIsIndlc3BlIiwibGF3aW5lIiwidGFibGV0dGUiLCJrcsO8Y2tlIiwiZ3JhemllIiwiZGl2YSIsInB1bGxlIiwibmVzc2VsIiwia2FrZXJsYWtlIiwiZGlzdGVsIiwiYW3DtmJlIiwiZmFja2VsIiwiaMO8ZnRlIiwicnVpbmUiLCJ3YWNodGVsIiwic2V1Y2hlIiwia2lwcGUiLCJzY2hpcHBlIiwiZ2VzdGFsdCIsIndvbGtlIiwibXVtaWUiLCJzcHVyIiwiY3JlbWUiLCJtb3R0ZSJdOw0KdmFyIGFfd29ydGxpc3RlNCA9IFsiYnVzY2giLCJmaW5rIiwibmFnZWwiLCJiYW1tZWwiLCJrbG9wcGVyIiwidGVudGFrZWwiLCJicsOkZ2VuIiwic2NobHVtcGYiLCJodXN0ZW4iLCJlcnNhdHoiLCJoYXVmZW4iLCJrbsO2ZGVsIiwicsO8c3NlbCIsImhpbnRlcm4iLCJlaW1lciIsInBpY2tlbCIsInN0dW1wZiIsImvDpHNlIiwibW9sY2giLCJrb2hsIiwiZ251YmJlbCIsInNhY2siLCJoYW5zZWwiLCJwdWxsZXIiLCJhbHB0cmF1bSIsImthc3RlbiIsImtvcGYiLCJiZXV0ZWwiLCJiZXdvaG5lciIsImt1Y2hlbiIsImZyZXVuZCIsIm5hc2NoZXIiLCJvcGEiLCJyb3R6Iiwia2x1bXBlbiIsInBldGVyIiwiaGFuc2VsIiwiYmVuZ2VsIiwia29sbGVnZSIsImZsZWNrIiwibMO2ZmZlbCIsImx1cmNoIiwiaG9iZWwiLCJzcGF0ZW4iLCJwdWRlbCIsInJldHRpY2giLCJyaW5uc3RlaW4iLCJ1bmZhbGwiLCJsYXBwZW4iLCJrw7xiZWwiLCJtb3BzIiwicGZvc3RlbiIsInp3ZXJnIiwicHVkZGluZyIsIm51Y2tlbCIsInB1dHplciIsImzDvG1tZWwiLCJiYXJvbiIsIm1vcCIsImJlc2VuIiwiZmV1ZGVsIiwiYm9semVuIiwicGlseiIsInN0aWVmZWwiLCJrw7Z0ZXIiLCJndWxsaSIsInBmcm9wZiIsInNjaHJhbmsiLCJrw7ZuaWciLCJwb3R0IiwicGFkZGVsIiwicmlubnN0ZWluIiwiemlua2VuIiwiaGFrZW4iLCJ3aXR6IiwiYnVja2VsIiwia25lY2h0IiwiZmFuIiwic2NobWFuZCIsImtsb3BzIiwiZ2F1bmVyIiwibHVsbGkiLCJncmF1cGUiLCJwaW1wZiIsImthc3BlciIsInNwcm9zcyIsInRldWZlbCIsImhhbW1lbCIsImJvY2siLCJzY2htb2RkZXIiLCJwcsO8Z2VsIiwic3BpZXNzZXIiLCJhYWwiLCJncm9zY2hlbiIsImdlaXN0Iiwicm9jaGVuIiwia25vY2hlbiIsImhvcnN0IiwicXVhcmsiLCJrZWtzIiwiemF1c2VsIiwiaWx0aXMiLCJqZWNrIiwiaG9uayIsInNwYXJnZWwiLCJuaXBwZWwiLCJhdHplIiwibXVmZmVsIiwiZ3JlaXMiLCJwaW7DtmtlbCIsImdlaGlsZmUiLCJoYWx1bmtlIiwibGF1Y2giLCJ0aMO2bGUiLCJvbmtlbCIsImtsZWNrcyIsInNjaGFkZW4iLCJhdXN3dXJmIiwiaGVycGVzIiwidW5yYXQiLCJhYmtsYXRzY2giLCJmbGVnZWwiLCJnbG90emVyIiwic3TDtnBzZWwiLCJyZXN0IiwidmVyc3VjaCIsImtvbXBvc3QiLCJmbHVjaCIsImpvZ3VydCIsInDDtm1wZWwiLCJzdGllbCIsImZldHplbiIsImR1c2NoZXIiLCJnbm9tIiwic2NobHVjayIsInNjaG51cGZlbiIsImluZmVrdCIsImluZmFya3QiLCJnZXJ1Y2giLCJyYW1ibyIsImRhY2tlbCIsInNjaHdpbmdlbCIsImRpZWIiLCJmbGFkZW4iLCJmbGF0c2NoZW4iLCJmdXNzZWwiLCJrbmlsY2giLCJmcm9zY2giLCJ3b21iYXQiLCJhbmZhbGwiLCJob2hsc2F1bSIsImJpbWJhbSIsIndvZGthIiwiZHVmdCIsImthZGF2ZXIiLCJiZWZhbGwiLCJlZ2VsIiwiZsOkbmdlciJdOw0KdmFyIGFfd29ydGxpc3RlNSA9IFsic2VrcmV0IiwiYmFsZyIsImJsYWciLCJtb25zdGVyIiwiZ2Vsw7Z0IiwiaW1pdGF0Iiwic2tlbGV0dCIsImRpbmciLCJ1bmRpbmciLCJhdWdlIiwiYnJvdCIsImRlbyIsImluc2VrdCIsImJpZXIiLCJtdXMiLCJlbmRlIiwiZnV0dGVyIiwiZ2V3w6RjaHMiLCJwcm9kdWt0IiwiZ2Vyw7ZsbCIsImJvbmJvbiIsImZ1cnVua2VsIiwicGFrZXQiLCJ2aXJ1cyIsImRlc2FzdGVyIiwic3TDvGNrIiwiZmFzcyIsInpldWciLCJmZXJrZWwiLCJlaSIsImdld2l0dGVyIiwiaG9ybW9uIiwiZXhwZXJpbWVudCIsImd1bGFzY2giLCJzY2huaXR6ZWwiLCJmZWxsIiwidGhlYXRlciIsInNjaGF1c3BpZWwiLCJiYWJ5Iiwic3BpZWx6ZXVnIiwiZ2VsIiwiZG9udXRsb2NoIiwiZ2VsZWUiLCJnZWx1bXBlIiwic2NoYWYiLCJtb2xla8O8bCIsImdld8O8cnoiLCJnZXNwZW5zdCIsImdlc3Bpbm5zdCIsIm1pdHRlbCIsImdlc2NobmV0eiIsIm9yZ2FuIiwicmlzb3R0byIsInZpZWgiLCJnZXPDpHNzIiwiZ2V6w7xjaHQiLCJla3plbSIsIm1vcGVkIiwiZ2Vyw7xtcGVsIiwiaGlybiIsImdlZsOkc3MiLCJ3YWNoc3R1bSIsIm1vbG9jaCIsInJpbm5zYWFsIiwiZ2VtZW5nZSIsIm9wb3NzdW0iLCJmcmV0dGNoZW4iLCJow6RobmNoZW4iLCJwbGFua3RvbiIsInVudGllciIsInVuZ2V0w7xtIiwiZ2VicsOkdSIsImZvbmR1ZSIsImJlaXNwaWVsIiwiZWxlbmQiLCJsZWlkIiwiZ2lmdCIsInZlcmRlcmJlbiIsInVuZ2zDvGNrIiwiZHJhbWEiLCJ0cmF1bWEiLCJ2ZXJzYWdlbiIsImZpYXNrbyIsImRpbGVtbWEiLCJkZWJha2VsIiwidGFidSIsImdlcsO8Y2h0IiwiaGluZGVybmlzIiwiZGluZ2RvbmciLCJkaW5nc2J1bXMiLCJnZXdpY2h0IiwiYWJ3YXNzZXIiLCJhYmJpbGQiLCJtb2RlbGwiLCJnZW3DpGxkZSIsImJyZXR0IiwiZ2ViYWxsZXIiLCJnZW3DpGNodCIsInRvdXBldCIsImdlc2Nod8OkdHoiLCJnZXJpcHBlIiwicGVjaCIsImxlaWRlbiIsInZlcmJyZWNoZW4iLCJmb3NzaWwiLCJzeW1wdG9tIiwiYmllc3QiLCJ3cmFjayIsImdlYsOkY2siLCJ1bmhlaWwiLCJ1bmdlbWFjaCIsIm9iamVrdCIsImdlc2ljaHQiLCJrb25mZWt0IiwiZ2VicmVjaGVuIiwibcOkcmNoZW4iLCJnZXLDpHQiLCJ2ZXJsdXN0Iiwic3luZHJvbSIsInN5bm9ueW0iLCJ3YXNzZXIiLCJ0aWVyIiwiZm9sbGlrZWwiLCJ1bmtyYXV0IiwidW5nZXppZWZlciIsImdldMO2c2UiLCJnZXNjaG1laXNzIiwiZ2Vicm9kZWwiLCJnZWpvZGVsIiwiaW5mZXJubyIsImdlcmljaHQiLCJtYWhsIiwia2FtZWwiLCJnZWJpc3MiLCJyZXB0aWwiLCJ2ZXJsaWVzcyIsInBhZGRlbCIsImdlYm90IiwibGFnZXIiLCJnZW1pc2NoIiwic2F1c2VuIiwiYW5nZWJvdCIsInppbW1lciIsIm3DtmJlbCIsInBhcmbDvG0iLCJwb2Rlc3QiLCJ1bmdlaGV1ZXIiLCJ6ZWljaGVuIiwidmVyc3RlY2siLCLDvGJlbCIsInNjcm90dW0iLCJlaXNlbiIsImJhbGxldHQiLCJsZWdvIiwiZ2VzZXR6IiwiZm9ybWF0IiwiYnVmZmV0IiwiZ3JhbnVsYXQiLCJkZXJpdmF0Il07DQogDQoNCi8vIFRlaWwgMjogRXJzdGVzIFdvcnQgZXJtaXR0ZWxuDQp2YXIgc19zY2hpbXBmd29ydCA9ICIiOw0KdmFyIGlfenVmYWxsc3phaGwgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhX3dvcnRsaXN0ZTEubGVuZ3RoKTsNCnNfc2NoaW1wZndvcnQgPSBhX3dvcnRsaXN0ZTFbaV96dWZhbGxzemFobF07DQovLyBHZW51cyBlcm1pdHRlbG4gLSBmZW1pbmluLCBtYXNrdWxpbiBvZGVyIE5ldXRydW0NCmlfZ2VudXMgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAzKTsNCi8vY29uc29sZS53YXJuKGlfZ2VudXMpOw0Kc3dpdGNoIChpX2dlbnVzKSB7DQogICAgY2FzZSAwOg0KICAgICAgICBzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArICIgIjsNCiAgICAgICAgYnJlYWs7DQogICAgY2FzZSAxOg0KICAgICAgICBzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArICJyICI7DQogICAgICAgIGJyZWFrOw0KICAgIGNhc2UgMjoNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyAicyAiOw0KICAgICAgICBicmVhazsNCiAgICBkZWZhdWx0Og0KICAgICAgICBjb25zb2xlLndhcm4oIkdlbnVzIGZhbHNjaGVyIFdlcnQ6IiArIGlfZ2VudXMpOw0KfQ0KDQovLyBUZWlsIDM6IFp3ZWl0ZXMgV29ydCBlcm1pdHRlbG4NCmlfenVmYWxsc3phaGwgPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBhX3dvcnRsaXN0ZTIubGVuZ3RoKSAtIDE7DQpzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArIGFfd29ydGxpc3RlMltpX3p1ZmFsbHN6YWhsXTsNCg0KLy8gVGVpbCA0OiAydGVyIFRlaWwgZGVzIDJ0ZW4gV29ydGVzIGVybWl0dGVsbg0Kc3dpdGNoIChpX2dlbnVzKSB7DQogICAgY2FzZSAwOg0KICAgICAgICBpX3p1ZmFsbHN6YWhsID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYV93b3J0bGlzdGUzLmxlbmd0aCkgLSAxOw0KICAgICAgICBzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArIGFfd29ydGxpc3RlM1tpX3p1ZmFsbHN6YWhsXTsNCiAgICAgICAgYnJlYWs7DQogICAgY2FzZSAxOg0KICAgICAgICBpX3p1ZmFsbHN6YWhsID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYV93b3J0bGlzdGU0Lmxlbmd0aCkgLSAxOw0KICAgICAgICBzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArIGFfd29ydGxpc3RlNFtpX3p1ZmFsbHN6YWhsXTsNCiAgICAgICAgYnJlYWs7DQogICAgY2FzZSAyOg0KICAgICAgICBpX3p1ZmFsbHN6YWhsID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYV93b3J0bGlzdGU1Lmxlbmd0aCkgLSAxOw0KICAgICAgICBzX3NjaGltcGZ3b3J0ID0gc19zY2hpbXBmd29ydCArIGFfd29ydGxpc3RlNVtpX3p1ZmFsbHN6YWhsXTsNCiAgICAgICAgYnJlYWs7DQogICAgZGVmYXVsdDoNCiAgICAgICAgY29uc29sZS53YXJuKCJHZW51cyBmYWxzY2hlciBXZXJ0OiIgKyBpX2dlbnVzKTsNCn0NCg0KDQoNCnJldHVybiBzX3NjaGltcGZ3b3J0Ow==</field>
        <comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
      </block>
      

      Die Funktion liefert immer genau einen Ausdruck zurück.
      Viel Spaß damit, euer Einarmiges Scherzdebakel


      Update 1:
      Hier eine Version die 1x die Minute ein neues Schimpfwort nach

      0_userdata.0.Schimpfwortgenerator.Schimpfwort
      
      <block xmlns="https://developers.google.com/blockly/xml" type="procedures_defcustomreturn" id="h!J2T(,rRS$uRe+#Tw8y" x="38" y="-337">
        <mutation statements="false"></mutation>
        <field name="NAME">Schimpfwortgenerator</field>
        <field name="SCRIPT">Ly8gRnJlaSBuYWNoIGh0dHBzOi8vZ2l0aHViLmNvbS9OaWtvbGFpUmFka2UvU2NoaW1wZm9saW5vL3dpa2kNCg0KLy8gVGVpbCAxOiBXb3J0bGlzdGVuIGRlZmluaWVyZW4NCnZhciBhX3dvcnRsaXN0ZTEgPSBbIkR1bXBmZSIsIlN0YXViaWdlIiwiTWllZmVuZGUiLCJTdGlua2VuZGUiLCJHYW1tbGlnZSIsIkhpbmtlbmRlIiwiV2luemlnZSIsIlBvcGVsaWdlIiwiTmFzc2UiLCJGdXJ6ZW5kZSIsIlJvc3RpZ2UiLCJIb2hsZSIsIlNpZmZpZ2UiLCJNaWVzZSIsIktydW1tZSIsIktsYXBwcmlnZSIsIlRyb2NrZW5lIiwiSGFhcmlnZSIsIlVyYWx0ZSIsIkdydW56ZW5kZSIsIlNjaHJlaWVuZGUiLCJNZWNrZXJuZGUiLCJOZXJ2ZW5kZSIsIlNhYmJlcm5kZSIsIlRyaWVmZW5kZSIsIk1vZHJpZ2UiLCJMdW1waWdlIiwiTGF1c2lnZSIsIlNpbm5sb3NlIiwiT2xsZSIsIlVubsO2dGlnZSIsIkRhbXBmZW5kZSIsIkxlZHJpZ2UiLCJFaW5hcm1pZ2UiLCJMZWVyZSIsIkzDpHN0aWdlIiwiSGV1bGVuZGUiLCJQaWNrZWxpZ2UiLCJGYXVsZSIsIlJhbnppZ2UiLCJUcsO8YmUiLCJEcmFsbGUiLCJCbGFua2UiLCJHaWVyaWdlIiwiVHJhbmlnZSIsIldhY2tlbG5kZSIsIlRvcmtlbG5kZSIsIlfDvHN0ZSIsIkZpc2NoaWdlIiwiQmVrbmFja3RlIiwiVmVya29ya3N0ZSIsIkhlaW1saWNoZSIsIkzDtmNocmlnZSIsIkJyb2NraWdlIiwiUGx1bXBlIiwiVGF0dHJpZ2UiLCJSYXR0ZXJuZGUiLCJTY2htdXR6aWdlIiwiTGlkZXJsaWNoZSIsIkTDtnNpZ2UiLCJQcm9sbGlnZSIsIkZpZXNlIiwiTXVmZmlnZSIsIk3DvGZmZWxuZGUiLCJQZWlubGljaGUiLCJOw7ZyZ2VsbmRlIiwiRmV0dGlnZSIsIlphaG5sb3NlIiwiRnJlY2hlIiwiU2Now6RiaWdlIiwiUGllZmlnZSIsIkd1bW1pZ2UiLCJMYWJicmlnZSIsIlBhdHppZ2UiLCJQZWx6aWdlIiwiUmV1ZGlnZSIsIlBla2lnZSIsIk3DvHJiZSIsIkhhcnppZ2UiLCJMYWhtZSIsIk1pY2tyaWdlIiwiQnLDpHNpZ2UiLCJab3R0ZWxpZ2UiLCJHZWxibGljaGUiLCJLbm9ycmlnZSIsIlNhbHppZ2UiLCJTY2hyaWxsZSIsIkR1c3NlbGlnZSIsIldpbmRpZ2UiLCJHcmF1c2lnZSIsIkdyw6Rzc2xpY2hlIiwiR3JvYmUiLCJTcGFja2lnZSIsIkthdXppZ2UiLCJGbGFjaHNpZ2UiLCJGcmFuc2lnZSIsIk1vdHppZ2UiLCJLYWhsZSIsIk5pZWRyaWdlIiwiS2VpZmVuZGUiLCJOaWNodGlnZSIsIkRyw7ZnZSIsIkZhZGUiLCJXZWluZW5kZSIsIkhpYmJlbGlnZSIsIlBsb2NraWdlIiwiQnJlbm5lbmRlIiwiRMO8cnJlIiwiS29jaGVuZGUiLCJLbmFyemVuZGUiLCJGYWx0aWdlIiwiU2NobGFtbWlnZSIsIkJyw7Zja2VsaWdlIiwiUmlzc2lnZSIsIlZlcmtlaW10ZSIsIkthbnRpZ2UiLCJHZWtsYXV0ZSIsIlF1aWVja2VuZGUiLCJGYXNlbG5kZSIsIkJlaXNzZW5kZSIsIkdlaMO2cm50ZSIsIlZlcmdlc3NlbmUiLCJCbGVpY2hlIiwiWndlaWNrZW5kZSIsIkZyb3N0aWdlIiwiTmFja2lnZSIsIkdydXNlbGlnZSIsIk1pbmRlcmUiLCJIYWdlcmUiLCJNYWdlcmUiLCJTY2h1cHBpZ2UiLCJCZWxlZ3RlIiwiU3TDpG5rZXJuZGUiLCJCw7ZzYXJ0aWdlIiwiUm9sbGVuZGUiLCJTY2hlY2tpZ2UiLCJSdWJiZWxuZGUiLCJTY2hpZWxlbmRlIiwiVHJhdHNjaGlnZSIsIkdyb3Rlc2tlIiwiQWJzdXJkZSIsIk1laGxpZ2UiLCJQbGF0dGUiLCJNw7xkZSIsIlRvdGFsZSIsIkJla2xvcHB0ZSIsIlNjaGF1cmlnZSIsIlRhdWJlIiwiQmV0w6R1YnRlIiwiQmVow6RtbWVydGUiLCJCZWxhbmdsb3NlIiwiQmVsZWlkaWd0ZSIsIkJldHJ1bmtlbmUiLCJCaXphcnJlIiwiRGlmZnVzZSJdOw0KdmFyIGFfd29ydGxpc3RlMiA9IFsiU3RhbXBmIiwiV2FiYmVsIiwiUHVwcyIsIlNjaG1hbHoiLCJTY2htaWVyIiwiSGFjayIsIlplbWVudCIsIlNwdWNrIiwiU3RhY2hlbCIsIktlbGxlciIsIkxhYmVyIiwiU3RvY2siLCJSdW56ZWwiLCJTY2hydW1wZiIsIkVrZWwiLCJTY2hub2RkZXIiLCJNYXRzY2giLCJXdXJtIiwiRWl0ZXIiLCJTcGVjayIsIk1pc3QiLCJLbG90eiIsIlfDvHJnIiwiTHVtcGVuIiwiU2NobGVpbSIsIld1cnN0IiwiRG9vZiIsIkJyYXQiLCJTY2h3YW1tIiwiS3JhdHoiLCJHcm90dGVuIiwiS3JpZWNoIiwiR2lmdCIsIlNjaGxhYmJlciIsIkfDtmJlbCIsIktsZWIiLCJTY2htYWRkZXIiLCJHcmluZCIsIkxhYmJlciIsIkx1ZnQiLCJNYXNzZW4iLCJTY2hpbW1lbCIsIk1pbmkiLCJPY2hzZW4iLCJQcm9ibGVtIiwiUXVhc3NlbCIsIlNjaG5hcHMiLCJTYWZ0IiwiRnJpZW1lbCIsIlphcHBlbCIsIlRyb3BmIiwiUGx1bnRzY2giLCJTdW1wZiIsIkhlY2tlbiIsIkdyYWIiLCJTY2h3aXR6IiwiU2NobmFyY2giLCJTY2hsZWljaCIsIlNjaGx1ZmYiLCJGbMO2dGVuIiwiSG9seiIsIktyZWlzY2giLCJEdWxsaSIsIkx1c2NoZW4iLCJHYW1tZWwiLCJBbHTDtmwiLCJSw7ZjaGVsIiwiR2xpYmJlciIsIkxhY2giLCJLcmFjaCIsIktuaWNrIiwiUXVldHNjaCIsIlF1YXRzY2giLCJRdWlldHNjaCIsIktuYXV0c2NoIiwiVMO8bXBlbCIsIlRlaWNoIiwiS25hdHRlciIsIlNhdWYiLCJQaXBpIiwiU3RydWxsZXIiLCJHcsOkdGVuIiwiTmFzZW4iLCJQZWNoIiwiTGVpZXIiLCJSZWllciIsIkJsw7ZkIiwiU2Nob3JmIiwiU2FiYmVsIiwiUXVlbmdlbCIsIkJhbmFuZW4iLCJVbnNpbm5zIiwiUGx1bnNjaCIsIkZydXN0IiwiTG90dGVyIiwiRnVtbWVsIiwiQmx1YmJlciIsIldvYmJlbCIsIlZvbGxiYXJ0IiwiTGFjayIsIktsbyIsIk1vZGVyIiwiS25pcnNjaCIsIlppdHRlciIsIkthbHQiLCJTY2hsw7xyZiIsIlNjaG5pZWYiLCJLbGVja2VyIiwiUnVtYmEiLCJTY2h3dXJiZWwiLCJTY2hyYWJiZWwiLCJTY2hsYXVjaCIsIlNjaHJ1bXBlbCIsIkjDvGhuZXIiLCJTY2hsYWNrZXIiLCJCcmFiYmVsIiwiS3JhbXBmIiwiUHLDvGdlbCIsIlJhcHBlbCIsIlp1cHBlbCIsIlBsdW5kZXIiLCJEb25uZXIiLCJSaWVzZW4iLCJCdXR0ZXIiLCJXaXNjaHdhc2NoIiwiUG9sdGVyIiwiVHJhbXBlbCIsIlNhdWVyIiwiSGFtcGVsIiwiQml0dGVyIiwiTWFzc2VsIiwiRmxpdHoiLCJXYXJtIiwiU2NobGluZyIsIlBsdW1wcyIsIlF1w6RsIiwiU3RyYW1wZWwiLCJTY2hsZWNrIiwiUmVjeWNsaW5nIiwiRWdhbCIsIkJsZWNoIiwiSG9ycm9yIiwiUnVtcGVsIiwiU2NobnVsbGVyIiwiU2NoZXJ6IiwiTmFja3QiLCJQYW1wZWwiLCJNb3Jhc3QiLCJGbGFjaCIsIkFuZ3N0IiwiU3BlaSIsIlB1bXBlbCIsIkF1c3NjaGxhZyIsIlF1YWxtIiwiUmFtYmF6YW1iYSIsIktsZWluIiwiU3BydWRlbCJdOw0KdmFyIGFfd29ydGxpc3RlMyA9IFsic3VwcGUiLCJzb2NrZSIsImJvbWJlIiwiYm91bGV0dGUiLCJzY2h3YXJ0ZSIsIndhcnplIiwiYmV1bGUiLCJwZXN0IiwicGZsYXVtZSIsInLDvGJlIiwiZ2VpZ2UiLCJyYXR0ZSIsImtyYW5raGVpdCIsInd1bmRlIiwib21hIiwic3R1bGxlIiwibGllc2UiLCJicnV0IiwiaGVubmUiLCJ6d2llYmVsIiwiYnVkZSIsImtpc3RlIiwiYnJhdXQiLCJsZXVjaHRlIiwia3LDtnRlIiwibnVzcyIsInNwaW5uZSIsImdydWJlIiwidG9pbGV0dGUiLCJrcmFrZSIsInBmw7x0emUiLCJiYWNrZSIsImJyYXRzY2hlIiwia2xhdHNjaGUiLCJudWRlbCIsImtub2xsZSIsInTDvHRlIiwibmFzZSIsIm1hZGUiLCJ0b25uZSIsImtyYW1wZSIsImLDvHJzdGUiLCJ3aW5kZWwiLCJzZW1tZWwiLCJoYXhlIiwiZ3LDpGZpbiIsInNjaGxldWRlciIsInppZXJkZSIsImtyw6RoZSIsImxhdHRlIiwibmlldGUiLCJyYXNzZWwiLCJhc3NlbCIsInRvcnRlIiwiZ2FsbGUiLCJsYXRzY2hlIiwic2NocnVsbGUiLCJrYW5vbmUiLCJibGFzZSIsInBlbGxlIiwidHJpbmUiLCJxdWVlbiIsInplY2tlIiwicHJhbGluZSIsIm1hZ3QiLCJwcmFjaHQiLCJmcml0dGUiLCJzb3NzZSIsImxhcnZlIiwibXVybWVsIiwiaGV4ZSIsInBhbXBlIiwic2lyZW5lIiwiZHLDvHNlIiwia2xldHRlIiwicGV0emUiLCJicnVtbWUiLCJnbGF0emUiLCJxdWFsbGUiLCJuYXR0ZXIiLCJrcmFsbGUiLCJ6aWVnZSIsImdyw7x0emUiLCJzw7xsemUiLCJudWxwZSIsIndhbXBlIiwiZnJpa2FkZWxsZSIsImZsdW5kZXIiLCJ0cnVsbGEiLCJ6aWNodGUiLCJ1c2NoaSIsImt1aCIsInBhcHBlIiwiaHVwZSIsInRyw7Z0ZSIsInNjaGFiZSIsImthbmFsbGllIiwic2NoYXJ0ZSIsInJpbGxlIiwiYW1zZWwiLCJhbGdlIiwibMO8Y2tlIiwiYnJlbXNlIiwibcO8Y2tlIiwiYsO8cnN0ZSIsIndhbm5lIiwicG9ja2UiLCJwbMO2cnJlIiwic2NoYWJyYWNrZSIsInd1cHBlIiwic2ljaGVsIiwidGFudGUiLCJyZXVzZSIsInJhdHNjaGUiLCJwYXVrZSIsImZsdXBwZSIsIm1hdHJvbmUiLCJodW1tZWwiLCJwYXJhZGUiLCJhdHRyYXBwZSIsImzDvGdlIiwiZmxvc3NlIiwiZnVuemVsIiwiZ3Vya2UiLCJwaWVwZSIsImfDtnJlIiwia29sYmVuIiwic2FtbWx1bmciLCJwcmltZWwiLCJvbW1lIiwibG90dGUiLCJ1bmtlIiwic3RyaXBwZSIsInNlaWZlIiwicGzDtnR6ZSIsIndlc3BlIiwibGF3aW5lIiwidGFibGV0dGUiLCJrcsO8Y2tlIiwiZ3JhemllIiwiZGl2YSIsInB1bGxlIiwibmVzc2VsIiwia2FrZXJsYWtlIiwiZGlzdGVsIiwiYW3DtmJlIiwiZmFja2VsIiwiaMO8ZnRlIiwicnVpbmUiLCJ3YWNodGVsIiwic2V1Y2hlIiwia2lwcGUiLCJzY2hpcHBlIiwiZ2VzdGFsdCIsIndvbGtlIiwibXVtaWUiLCJzcHVyIiwiY3JlbWUiLCJtb3R0ZSJdOw0KdmFyIGFfd29ydGxpc3RlNCA9IFsiYnVzY2giLCJmaW5rIiwibmFnZWwiLCJiYW1tZWwiLCJrbG9wcGVyIiwidGVudGFrZWwiLCJicsOkZ2VuIiwic2NobHVtcGYiLCJodXN0ZW4iLCJlcnNhdHoiLCJoYXVmZW4iLCJrbsO2ZGVsIiwicsO8c3NlbCIsImhpbnRlcm4iLCJlaW1lciIsInBpY2tlbCIsInN0dW1wZiIsImvDpHNlIiwibW9sY2giLCJrb2hsIiwiZ251YmJlbCIsInNhY2siLCJoYW5zZWwiLCJwdWxsZXIiLCJhbHB0cmF1bSIsImthc3RlbiIsImtvcGYiLCJiZXV0ZWwiLCJiZXdvaG5lciIsImt1Y2hlbiIsImZyZXVuZCIsIm5hc2NoZXIiLCJvcGEiLCJyb3R6Iiwia2x1bXBlbiIsInBldGVyIiwiaGFuc2VsIiwiYmVuZ2VsIiwia29sbGVnZSIsImZsZWNrIiwibMO2ZmZlbCIsImx1cmNoIiwiaG9iZWwiLCJzcGF0ZW4iLCJwdWRlbCIsInJldHRpY2giLCJyaW5uc3RlaW4iLCJ1bmZhbGwiLCJsYXBwZW4iLCJrw7xiZWwiLCJtb3BzIiwicGZvc3RlbiIsInp3ZXJnIiwicHVkZGluZyIsIm51Y2tlbCIsInB1dHplciIsImzDvG1tZWwiLCJiYXJvbiIsIm1vcCIsImJlc2VuIiwiZmV1ZGVsIiwiYm9semVuIiwicGlseiIsInN0aWVmZWwiLCJrw7Z0ZXIiLCJndWxsaSIsInBmcm9wZiIsInNjaHJhbmsiLCJrw7ZuaWciLCJwb3R0IiwicGFkZGVsIiwicmlubnN0ZWluIiwiemlua2VuIiwiaGFrZW4iLCJ3aXR6IiwiYnVja2VsIiwia25lY2h0IiwiZmFuIiwic2NobWFuZCIsImtsb3BzIiwiZ2F1bmVyIiwibHVsbGkiLCJncmF1cGUiLCJwaW1wZiIsImthc3BlciIsInNwcm9zcyIsInRldWZlbCIsImhhbW1lbCIsImJvY2siLCJzY2htb2RkZXIiLCJwcsO8Z2VsIiwic3BpZXNzZXIiLCJhYWwiLCJncm9zY2hlbiIsImdlaXN0Iiwicm9jaGVuIiwia25vY2hlbiIsImhvcnN0IiwicXVhcmsiLCJrZWtzIiwiemF1c2VsIiwiaWx0aXMiLCJqZWNrIiwiaG9uayIsInNwYXJnZWwiLCJuaXBwZWwiLCJhdHplIiwibXVmZmVsIiwiZ3JlaXMiLCJwaW7DtmtlbCIsImdlaGlsZmUiLCJoYWx1bmtlIiwibGF1Y2giLCJ0aMO2bGUiLCJvbmtlbCIsImtsZWNrcyIsInNjaGFkZW4iLCJhdXN3dXJmIiwiaGVycGVzIiwidW5yYXQiLCJhYmtsYXRzY2giLCJmbGVnZWwiLCJnbG90emVyIiwic3TDtnBzZWwiLCJyZXN0IiwidmVyc3VjaCIsImtvbXBvc3QiLCJmbHVjaCIsImpvZ3VydCIsInDDtm1wZWwiLCJzdGllbCIsImZldHplbiIsImR1c2NoZXIiLCJnbm9tIiwic2NobHVjayIsInNjaG51cGZlbiIsImluZmVrdCIsImluZmFya3QiLCJnZXJ1Y2giLCJyYW1ibyIsImRhY2tlbCIsInNjaHdpbmdlbCIsImRpZWIiLCJmbGFkZW4iLCJmbGF0c2NoZW4iLCJmdXNzZWwiLCJrbmlsY2giLCJmcm9zY2giLCJ3b21iYXQiLCJhbmZhbGwiLCJob2hsc2F1bSIsImJpbWJhbSIsIndvZGthIiwiZHVmdCIsImthZGF2ZXIiLCJiZWZhbGwiLCJlZ2VsIiwiZsOkbmdlciJdOw0KdmFyIGFfd29ydGxpc3RlNSA9IFsic2VrcmV0IiwiYmFsZyIsImJsYWciLCJtb25zdGVyIiwiZ2Vsw7Z0IiwiaW1pdGF0Iiwic2tlbGV0dCIsImRpbmciLCJ1bmRpbmciLCJhdWdlIiwiYnJvdCIsImRlbyIsImluc2VrdCIsImJpZXIiLCJtdXMiLCJlbmRlIiwiZnV0dGVyIiwiZ2V3w6RjaHMiLCJwcm9kdWt0IiwiZ2Vyw7ZsbCIsImJvbmJvbiIsImZ1cnVua2VsIiwicGFrZXQiLCJ2aXJ1cyIsImRlc2FzdGVyIiwic3TDvGNrIiwiZmFzcyIsInpldWciLCJmZXJrZWwiLCJlaSIsImdld2l0dGVyIiwiaG9ybW9uIiwiZXhwZXJpbWVudCIsImd1bGFzY2giLCJzY2huaXR6ZWwiLCJmZWxsIiwidGhlYXRlciIsInNjaGF1c3BpZWwiLCJiYWJ5Iiwic3BpZWx6ZXVnIiwiZ2VsIiwiZG9udXRsb2NoIiwiZ2VsZWUiLCJnZWx1bXBlIiwic2NoYWYiLCJtb2xla8O8bCIsImdld8O8cnoiLCJnZXNwZW5zdCIsImdlc3Bpbm5zdCIsIm1pdHRlbCIsImdlc2NobmV0eiIsIm9yZ2FuIiwicmlzb3R0byIsInZpZWgiLCJnZXPDpHNzIiwiZ2V6w7xjaHQiLCJla3plbSIsIm1vcGVkIiwiZ2Vyw7xtcGVsIiwiaGlybiIsImdlZsOkc3MiLCJ3YWNoc3R1bSIsIm1vbG9jaCIsInJpbm5zYWFsIiwiZ2VtZW5nZSIsIm9wb3NzdW0iLCJmcmV0dGNoZW4iLCJow6RobmNoZW4iLCJwbGFua3RvbiIsInVudGllciIsInVuZ2V0w7xtIiwiZ2VicsOkdSIsImZvbmR1ZSIsImJlaXNwaWVsIiwiZWxlbmQiLCJsZWlkIiwiZ2lmdCIsInZlcmRlcmJlbiIsInVuZ2zDvGNrIiwiZHJhbWEiLCJ0cmF1bWEiLCJ2ZXJzYWdlbiIsImZpYXNrbyIsImRpbGVtbWEiLCJkZWJha2VsIiwidGFidSIsImdlcsO8Y2h0IiwiaGluZGVybmlzIiwiZGluZ2RvbmciLCJkaW5nc2J1bXMiLCJnZXdpY2h0IiwiYWJ3YXNzZXIiLCJhYmJpbGQiLCJtb2RlbGwiLCJnZW3DpGxkZSIsImJyZXR0IiwiZ2ViYWxsZXIiLCJnZW3DpGNodCIsInRvdXBldCIsImdlc2Nod8OkdHoiLCJnZXJpcHBlIiwicGVjaCIsImxlaWRlbiIsInZlcmJyZWNoZW4iLCJmb3NzaWwiLCJzeW1wdG9tIiwiYmllc3QiLCJ3cmFjayIsImdlYsOkY2siLCJ1bmhlaWwiLCJ1bmdlbWFjaCIsIm9iamVrdCIsImdlc2ljaHQiLCJrb25mZWt0IiwiZ2VicmVjaGVuIiwibcOkcmNoZW4iLCJnZXLDpHQiLCJ2ZXJsdXN0Iiwic3luZHJvbSIsInN5bm9ueW0iLCJ3YXNzZXIiLCJ0aWVyIiwiZm9sbGlrZWwiLCJ1bmtyYXV0IiwidW5nZXppZWZlciIsImdldMO2c2UiLCJnZXNjaG1laXNzIiwiZ2Vicm9kZWwiLCJnZWpvZGVsIiwiaW5mZXJubyIsImdlcmljaHQiLCJtYWhsIiwia2FtZWwiLCJnZWJpc3MiLCJyZXB0aWwiLCJ2ZXJsaWVzcyIsInBhZGRlbCIsImdlYm90IiwibGFnZXIiLCJnZW1pc2NoIiwic2F1c2VuIiwiYW5nZWJvdCIsInppbW1lciIsIm3DtmJlbCIsInBhcmbDvG0iLCJwb2Rlc3QiLCJ1bmdlaGV1ZXIiLCJ6ZWljaGVuIiwidmVyc3RlY2siLCLDvGJlbCIsInNjcm90dW0iLCJlaXNlbiIsImJhbGxldHQiLCJsZWdvIiwiZ2VzZXR6IiwiZm9ybWF0IiwiYnVmZmV0IiwiZ3JhbnVsYXQiLCJkZXJpdmF0Il07DQogDQovLyBUZWlsIDI6IEVyc3RlcyBXb3J0IGVybWl0dGVsbg0KdmFyIHNfc2NoaW1wZndvcnQgPSAiIjsNCnZhciBpX3p1ZmFsbHN6YWhsID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYV93b3J0bGlzdGUxLmxlbmd0aCk7DQpzX3NjaGltcGZ3b3J0ID0gYV93b3J0bGlzdGUxW2lfenVmYWxsc3phaGxdOw0KLy8gR2VudXMgZXJtaXR0ZWxuIC0gZmVtaW5pbiwgbWFza3VsaW4gb2RlciBOZXV0cnVtDQppX2dlbnVzID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMyk7DQovL2NvbnNvbGUud2FybihpX2dlbnVzKTsNCnN3aXRjaCAoaV9nZW51cykgew0KICAgIGNhc2UgMDoNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyAiICI7DQogICAgICAgIGJyZWFrOw0KICAgIGNhc2UgMToNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyAiciAiOw0KICAgICAgICBicmVhazsNCiAgICBjYXNlIDI6DQogICAgICAgIHNfc2NoaW1wZndvcnQgPSBzX3NjaGltcGZ3b3J0ICsgInMgIjsNCiAgICAgICAgYnJlYWs7DQogICAgZGVmYXVsdDoNCiAgICAgICAgY29uc29sZS53YXJuKCJHZW51cyBmYWxzY2hlciBXZXJ0OiIgKyBpX2dlbnVzKTsNCn0NCg0KLy8gVGVpbCAzOiBad2VpdGVzIFdvcnQgZXJtaXR0ZWxuDQppX3p1ZmFsbHN6YWhsID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogYV93b3J0bGlzdGUyLmxlbmd0aCkgLSAxOw0Kc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyBhX3dvcnRsaXN0ZTJbaV96dWZhbGxzemFobF07DQoNCi8vIFRlaWwgNDogMnRlciBUZWlsIGRlcyAydGVuIFdvcnRlcyBlcm1pdHRlbG4NCnN3aXRjaCAoaV9nZW51cykgew0KICAgIGNhc2UgMDoNCiAgICAgICAgaV96dWZhbGxzemFobCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFfd29ydGxpc3RlMy5sZW5ndGgpIC0gMTsNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyBhX3dvcnRsaXN0ZTNbaV96dWZhbGxzemFobF07DQogICAgICAgIGJyZWFrOw0KICAgIGNhc2UgMToNCiAgICAgICAgaV96dWZhbGxzemFobCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFfd29ydGxpc3RlNC5sZW5ndGgpIC0gMTsNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyBhX3dvcnRsaXN0ZTRbaV96dWZhbGxzemFobF07DQogICAgICAgIGJyZWFrOw0KICAgIGNhc2UgMjoNCiAgICAgICAgaV96dWZhbGxzemFobCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGFfd29ydGxpc3RlNS5sZW5ndGgpIC0gMTsNCiAgICAgICAgc19zY2hpbXBmd29ydCA9IHNfc2NoaW1wZndvcnQgKyBhX3dvcnRsaXN0ZTVbaV96dWZhbGxzemFobF07DQogICAgICAgIGJyZWFrOw0KICAgIGRlZmF1bHQ6DQogICAgICAgIGNvbnNvbGUud2FybigiR2VudXMgZmFsc2NoZXIgV2VydDoiICsgaV9nZW51cyk7DQp9DQoNCg0KDQpyZXR1cm4gc19zY2hpbXBmd29ydDs=</field>
        <comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
      </block>
      

      Edit: Hatte den Datenpunkt schreibgeschützt erstellt aber dann unbestätigt geschrieben - Skript korrigiert (gibt sonst Fehlermeldung im Log)

      posted in Skripten / Logik
      BananaJoe
      BananaJoe
    • RE: Strom sparen mit Fully Browser und Samsung Tablet

      @heinzie sagte in Strom sparen mit Fully Browser und Samsung Tablet:

      Was mir aber nicht so gefällt es der relativ hohe Stromverbrauch. Dazu mal ein paar Fragen:

      Definiere das doch mal, über wie viele Wh / kWh sprechen wir?

      Prinzipiell sind solche Geräte schon - im Verhältnis zu dem was diese tun - schon sparsam. Mit schnellen und langsamen Kernen. Und das Apps automatisch beendet oder in den Schlafmodus geschickt werden.

      Nun ist ein Tablet als Anzeige und die Software dazu genau das Gegenteil. In der Regel ist das Display einer der größten Stromfresser und der Fully muss alle Möglichkeiten und Tricks anwenden damit er eben nicht beendet oder in den Sleep geschickt wird. Schließlich soll der ständig lauschen und auf Befehle von außerhalb reagieren.

      Deshalb muss man einen Tod sterben. Und bevor man da nun am Stromverbrauch des Tablets herumschraubt: Wieviel verbraucht es denn so am Tag? Ohne Optimierungen, macht aber alles was es soll?

      Ich habe mehrere Amazon Fire-Tablets im Einsatz, ohne Sparmaßnahmen, Bildschirm immer an, volle Helligkeit, 24x7 in Betrieb.
      Eines verbraucht 3W im Dauerbetrieb, Energie gestern steht auf 0,057kWh.
      Ich zahle noch 40Cent pro kWh, pro Jahr verbraucht ein Tablet bei mir also 10,51 Euro wenn der Strom tatsächlich komplett bezogen würde, in der Realität dank Solar nicht mal die Hälfte.
      bei 30 Cent pro kWh wären es 7,88 Euro.

      Den Preis zahle ich für den Luxus das die Tablets alle ständig an sind, die aktuellen Werte anzeigen, sofort bedienbar sind. Und gleichzeitig das Nachtlicht 🙂

      posted in Off Topic
      BananaJoe
      BananaJoe
    • RE: iot Alexa Tutorial

      @nullo83 ich schreibe das gleiche wie @SwissMen , nur auf anderen Weg:

      Der IoT-Adapter kann beliebige Datenpunkte als Alexa-Gerät (über den ioBroker-Skill) bereitstellen.
      Den IoT-Adapter sowie den Skill unter Alexa hast du ja schon eingerichtet - prima.

      Unter Instanzen => iot.0 => Einstellungen kommst du in die Konfiguration:
      94134e17-6f13-4e72-8b70-af7177deb240-image.png

      Da gibt es den Reiter "Intelligente Aufzählungen" - alles was du dort siehst sind vermutlich die Geräte die du schon in Alexa hast / die Alexa schon gefunden hat. Ich nutze das nicht und habe dort alles deaktiviert:
      21734cb8-f590-4bba-bd1e-4269795d55a9-image.png
      Was diese Aufzählungen sind und wie diese Funktionieren muss dir jemand anders Erklären. Richtig eingerichtet würden neue Geräte halt automatisch auftauchen. Da aber alles selber geskripted habem habe ich das alles deaktiviert.

      Interessant ist der Reiter Alexa-Geräte:
      61bd4c7a-ff7a-422e-8fdf-964735d56c6c-image.png
      Dort kannst du beliebige Datenpunkte verknüpfen.
      Steckdosen und Lampen in der Regel als True/False Datenpunkt (an oder aus), der Datenpunkt kann aus einem Adapter stammen oder ein von dir erstellter sein (in der Regeln dann unterhalb von 0_userdata.0

      Ich nehme hier mal den folgenden:

      0_userdata.0.AlarmSystem.Global.active
      

      Du gehst oben auf den +, dann öffnet sich der Dialog wo du den Datenpunkt auswählen kannst:
      46c2c05c-2140-45c0-9d33-2368d5d1af67-image.png
      Bestätigen und einen Moment warten, es geht ein neues Fenster auf:
      239cdca7-308c-4751-abda-b8faa245dbc1-image.png

      Hier kannst du den Smart-Name angeben - unter dem Namen kannst du den später (gleich im Anschluss!) über Alexa ansprechen. Und den Typ, der ist unter anderem für das Symbol in Alexa notwendig und ob Alexa den als Lampe behandelt. Wenn du mit OK Speicherst siehts du wie der Eintrag entsteht und mit der Cloud synchronisiert wird. Der wird zunächst grün
      d680ece9-bfda-4d84-9a74-53bd016dde82-image.png
      und wenn das Grüne weg ist, ist es auch mit der Cloud synchronisiert. Jetzt kannst du Alexa nach neuen Geräten suchen lassen und er wird das Gerät finden.
      Schaltest du es per Alexa ein oder aus so wird der Datenpunkt auf True oder False / Wahr oder falsch gesetzt.
      Und das Geräte schaltet (Datenpunkt aus einem Adapter) oder dein Skript springt an (eigener Datenpunkt).

      Hat ein Gerät mehrere Eigenschaften, z.B. eine Lampe die man Ein-/Ausschalten kannst aber auch Dimmen (0 bis 100%), legst du die Lampe einfach 2x hier an, beide male unter dem exakt gleichen Namen, beide male als Lampe. Der True/False Datenpunkt muss dann aber POWER heißen, der Datenpunkt für die Helligkeit Dimmer. Er macht dann eine Gruppe daraus:
      389e06a6-79e4-482e-a455-6883e6444347-image.png

      Der IoT-Adapter schaltet nur. Den kannst du nicht nutzen um den Status abzufragen bzw. in Alexa wird er dir den Status auch nicht anzeigen können.

      Den Alexa-Adapter in ioBroker brauchst du nicht unbedingt dafür, den brauchst du wenn du Alexa von ioBroker aus steuern willst, z.B. um per Text ein Sprachkommando zu senden.

      Edit: Typos korrigiert

      posted in Cloud Dienste
      BananaJoe
      BananaJoe
    • RE: Kann man ein Tuya-Gerät ohne Cloud mit ioBroker verbinden?

      @codierknecht sagte in Kann man ein Tuya-Gerät ohne Cloud mit ioBroker verbinden?:

      @haselchen sagte in Kann man ein Tuya-Gerät ohne Cloud mit ioBroker verbinden?:

      Abmessungen: 60 × 60 × 49,2 mm

      Der steht halt ziemlich vor. Meine Holde würde da mit ziemlicher Sicherheit etwas flacheres bevorzugen.
      Aber 'ne Alternative wäre der sicher. Kann man zur Not auch unauffällig montieren.

      Das Design hat mal den "Red Dot Award" gewonnen, das kann deine Frau doch nicht ignorieren!

      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • RE: Akku ausschließlich über Netzstrom laden

      Es soll Menschen geben, die haben ein paar Pylontech-Akkus im Keller (oder LiFePO4 Selbstbau-Akkus), laden diese per 48V Ladegeräte die per Tasmota-Plugs ein und ausgeschaltet werden, oder besser noch mit einem (oder mehreren) Huawei R4850-G2 Netzteilen die per ESP8266 geregelt werden und dann genau soviel einspeisen wie der Hichi am Stromzähler oder Shelly 3EM als Überschuss ausweisen:

      • https://github.com/KlausLi/Esp-HuaweiR4850-Controller
      • https://www.youtube.com/watch?v=NC1X--fSeu0

      Und zum Einspeisen nimmt man 1 bis 3 SoyouSource Wechselrichter, die kann man auf Batteriebetrieb stellen. Es liegt eine Strommessklammer dabei mit welcher einer genau den Strom einer Phase ausgleichen kann.
      Alternativ steuert man die ebenfalls mit einem ESP8266 und dann wird genau soviel eingespeist wie der Hichi am Stromzähler oder Shelly 3EM als Verbrauch melden (und auf nahezu 0 geregelt)

      • https://github.com/KlausLi/Esp-Soyosource-Controller
      • https://www.youtube.com/watch?v=zpDsxDEU1P0

      Also ich habe gehört das manche das wohl so machen ...

      posted in Hardware
      BananaJoe
      BananaJoe
    • RE: [Vorlage] Schimpfwortgenerator

      Oh ha, jetzt bin ich berühmt ... dafür Blockly genutzt zu haben ...

      posted in Skripten / Logik
      BananaJoe
      BananaJoe
    • RE: E-INK Display OpenEPaperLink - Displayanzeige mit Batterie

      @beowolf sagte in EPS E-INK Display Ansteuerung -> Statusdisplay für 2€:

      Ich bin ein wenig Ratlos und weiß nicht wo ich anfangen soll.

      Hier mal grob in Schritten zusammengefasst:

      Schritt 1: Eigene VIS für die ePaper Tags
      Im VIS Editor hatte ich ein neues Projekt erstellt - hier openepaper genannt:
      5fae74ac-f912-4ad3-a207-09f96b1b3a78-image.png

      In dem Projekt habe ich dann jeweils eine Seite für jedes Tag angelegt. Hier zum Beispiel für den Briefkasten, ein 2,9" Tag:
      5a77a407-e84b-4256-9077-6fb1e0ac58a5-image.png
      Wie man sieht habe ich unter Tools für diese View eine Auflösung von 296x128 Pixel eingestellt. Das habe ich gemacht weil er mir dann den roten Rahmen um diesen Bereich malt und ich weiß wo/wie ich die Elemente platzieren muss.

      Man kann auch beliebige Elemente außerhalb des roten Rahmens platzieren, die sind dann aber später halt nicht zu sehen. Hier habe ich das für einen Informationstext genutzt und ich parke auch nicht genutzte Elemente die ich aber vielleicht noch mal brauche so. Hier ist also das Batteriesymbol und die Prozentanzeige in Zahl und als Balken zu sehen.
      Die 2,9" Tags könnten 3 Farben: Rot (#FF0000), Schwarz (#000000) und Weiß (#FFFFFF), also am besten die Elemente in dieser Mini-View in diesen 3 Farben halten.

      Diese Seite hat später eine URL die man sich ja auch über die Play-Schaltfläche oben rechts ansehen kann (Runtime in einem neuen Fenster aufmachen). Bei mir ist die URL die folgende

      http://192.168.1.8:8082/vis/index.html?openepaper#Briefkasten
      

      Drückt auf den Play Button und kopiert euch die URL einfach dort heraus wenn wir diese dann brauchen.

      Schritt 2: Puppeteer installieren wenn noch nicht geschehen
      Installiert den Adapter Puppenspieler
      9241e08b-58b8-4687-a5d5-06c92562930c-image.png
      und stellt sicher das dieser dann auch eine laufende Instanz hat:
      afa80031-0a51-46f8-93ed-1a66e18696eb-image.png
      Sollte der Adapter nicht auf Grün springen (weil ihr das ganze z.B. auf einem Raspberry Pi laufen habt) so beachtet bitte diesen Issue hier, da wird beschrieben wie Ihr den dann trotzdem zum laufen bekommt: https://github.com/foxriver76/ioBroker.puppeteer/issues/13

      Schritt 3: Blockly-Skript das die VIS-View an das ePaper Tag sendet
      Ich beschreibe das hier für Blockly, wer das nicht nutzt kann sich ja den passenden Code herauskopieren.
      Das ganze habe ich mir auch nicht alleine ausgedacht sondern ist die Sammlung aus den anderen Artikel hier (und wahrscheinlich anderen aus dem Forum hier).

      Das Aufrufen von Puppeteer habe ich in eine eigene Funktion gepackt. Es holt den Screenshot und sendet diesen auch gleich an das ePaper-Tag.
      Erstellt am besten ein neues Blockly-Skript und importiert diese Vorlage:

      <block xmlns="https://developers.google.com/blockly/xml" type="procedures_defcustomnoreturn" id="od4:#LN4MMPOX5Cuwv0G" x="63" y="-187">
        <mutation statements="false">
          <arg name="imageSaveToFilenameWithPath" varid="#6wGK,7)K9MvKS1Y^m7w"></arg>
          <arg name="urlOfVISView" varid="/34/q/S9,6lBaN0B^0p+"></arg>
          <arg name="viewWidth" varid="VtK5T):T+/?(:RTltehP"></arg>
          <arg name="viewHeight" varid="d%Y)z(!gms*2[:-Q?srZ"></arg>
          <arg name="cutoutX" varid="yI1;;SNF[v//PXeFn75c"></arg>
          <arg name="cutoutY" varid="ixza1C+:$RS#_aO(+mTw"></arg>
          <arg name="cutoutWidth" varid="JHgH!or}TSYU@4ok~:dD"></arg>
          <arg name="cutoutHeight" varid="pui*x3;[n/WA5nodRtex"></arg>
          <arg name="jpgQuality" varid="{l]%ubA0HMv*]m|`-bQ#"></arg>
          <arg name="waitForSelector" varid="^}k9.{.g,!Q=P%/G;/%p"></arg>
          <arg name="imageUploadURL" varid="O}nZ-9?%oQZ/=~9D;8MW"></arg>
          <arg name="ePaperMAC" varid="$iC+L#(86gFYKS_:{d3%"></arg>
        </mutation>
        <field name="NAME">ScreenshotVISandUpload2OpenEPaperLink</field>
        <field name="SCRIPT">c2VuZFRvKCdwdXBwZXRlZXIuMCcsICdzY3JlZW5zaG90JywgeyANCiAgICB1cmw6IHVybE9mVklTVmlldywgICAgICAgICAgICAgICAgICAgICAgLyogVVJMIGRlciBXZWJzZWl0ZSBhdXMgd2VsY2hlciBkZXIgU2NyZWVuc2hvdCBlcnN0ZWxsdCB3ZXJkZW4gc29sbCAqLw0KICAgIHBhdGg6IGltYWdlU2F2ZVRvRmlsZW5hbWVXaXRoUGF0aCwgICAgICAvKiBEYXRlaW5hbWUgdW5kIFBmYWQgZsO8ciBsb2thbGVzIFNwZWljaGVybiBkZXMgQmlsZGVzICovDQogICAgd2lkdGg6IHZpZXdXaWR0aCwgICAgICAgICAgICAgICAgICAgICAgIC8qIEJyZWl0ZSBkZXMgdmlydHVlbGxlbiBCcm93c2VyZmVuc3RlcnMgKi8NCiAgICBoZWlnaHQ6IHZpZXdIZWlnaHQsICAgICAgICAgICAgICAgICAgICAgLyogSMO2aGUgZGVzIHZpcnR1ZWxsZW4gQnJvd3NlcmZlbnN0ZXJzICovDQogICAgcXVhbGl0eToganBnUXVhbGl0eSwgICAgICAgICAgICAgICAgICAgIC8qIFF1YWxpdMOkdCBkZXMgSlBHLCBzb2xsdGUgaW1tZXIgMTAwIHNlaW4gKi8NCg0KICAgIHdhaXRPcHRpb246IHsNCiAgICAgICAgd2FpdEZvclNlbGVjdG9yOiB3YWl0Rm9yU2VsZWN0b3IsICAgLyogV2FydGV0IGZvciBkZW0gU2NyZWVuc2hvdCBiaXMgZGllc2VzIE9iamVrdCBzaWNodGJhciBpc3QgKi8NCiAgICAgICAgd2FpdEZvclRpbWVvdXQ6IDEwMDAwICAgICAgICAgICAgICAgLyogVGltZW91dCwgc29sYW5nZSB3aXJkIGF1ZiBkYXMgT2JqZWt0IGdld2FydGV0ICovDQogICAgfSwNCg0KICAgIGZ1bGxQYWdlOiBmYWxzZSwgICAgICAgICAgICAgICAgICAgICAgICAvKiBUcnVlIHfDvHJkZSBkaWUgZ2FuemUgU2VpdGUgU25hcHNob3RlbiB1bmQgZGllIGNsaXAtQW53ZWlzdW5nZW4gaWdub3JpZXJlbiAqLw0KDQogICAgY2xpcDogeyAgICAgICAgIA0KICAgICAgICB4OiBjdXRvdXRYLCAgICAgICAgICAgICAgICAgICAgICAgICAvKiBLb29yZGluYXRlIFggYW4gZGVyIGRlciBBdXNzY2huaXR0IGJlZ2lubmVuIHNvbGwgKi8NCiAgICAgICAgeTogY3V0b3V0WSwgICAgICAgICAgICAgICAgICAgICAgICAgLyogS29vcmRpbmF0ZSBZIGFuIGRlciBkZXIgQXVzc2Nobml0dCBiZWdpbm5lbiBzb2xsICovDQogICAgICAgIHdpZHRoOiBjdXRvdXRXaWR0aCwgICAgICAgICAgICAgICAgIC8qIEJyZWl0ZSBkZXMgQXVzc2Nobml0dGVzICovDQogICAgICAgIGhlaWdodDogY3V0b3V0SGVpZ2h0ICAgICAgICAgICAgICAgIC8qIEjDtmhlIGRlcyBBdXNzY2huaXR0ZXMgKi8NCiAgICAgIH0NCn0sIG9iaiA9PiB7DQogICAgICBpZiAob2JqLmVycm9yKSB7DQogICAgICAgIGNvbnNvbGUud2FybigiRmVobGVyIGJlaW0gQXVmcnVmIGRlciBWaWV3OiAiICsgdXJsT2ZWSVNWaWV3ICsgIiA9PiAiICsgb2JqLmVycm9yLm1lc3NhZ2UpOw0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAvKiBpbiBvYmoucmVzdWx0IHNpbmQgZGllIEJpbsOkcmRhdGVuIGRlcyBCaWxkZXMgKi8NCiAgICAgICAgdmFyIHJlcXVlc3QgPSByZXF1aXJlKCdyZXF1ZXN0Jyk7DQogICAgICAgIHZhciBvcHRpb25zID0gew0KICAgICAgICAgICAgdXJsOiBpbWFnZVVwbG9hZFVSTCwNCiAgICAgICAgICAgIG1ldGhvZDogJ1BPU1QnLA0KICAgICAgICAgICAgZm9ybURhdGE6IHsgDQogICAgICAgICAgICAgICAgImRpdGhlciI6ICIwIiwgDQogICAgICAgICAgICAgICAgIm1hYyI6IGVQYXBlck1BQywNCiAgICAgICAgICAgICAgICAiaW1hZ2UiOiBvYmoucmVzdWx0IH0sDQogICAgICAgIH07DQogICAgICAgIC8qIHVuZCBkaWUgVVJMIGF1ZnJ1ZmVuOiAqLw0KICAgICAgICByZXF1ZXN0KG9wdGlvbnMsZnVuY3Rpb24gKGVycm9yLCByZXNwb25zZSwgYm9keSl7DQogICAgICAgICAgICAvL2xvZygiTG9naW4tQW50d29ydDogIiArIEpTT04uc3RyaW5naWZ5KHJlc3BvbnNlKSk7DQogICAgICAgICAgICAvL2NvbnNvbGUud2FybigiSW1hZ2VVcGxvYWQ6ICIgKyBKU09OLnN0cmluZ2lmeShyZXNwb25zZSkpOw0KICAgICAgICAgICAgY29uc29sZS5sb2coIkltYWdlOiIgKyBpbWFnZVNhdmVUb0ZpbGVuYW1lV2l0aFBhdGggKyAiID0+IEltYWdlVXBsb2FkU3RhdHVzQ29kZTogIiArIHJlc3BvbnNlLnN0YXR1c0NvZGUpOw0KICAgICAgICAgICAgLyoNCiAgICAgICAgICAgIGlmKHBhcnNlSW50KGJvZHkuZXJyb3JfY29kZSk9PTApIHsNCiAgICAgICAgICAgICAgICAvLyBuaXgNCiAgICAgICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICAgIC8vIHdpZWRlciBuaXgNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgICovDQogICAgICAgIH0pOw0KICAgICAgfQ0KfSk7DQo=</field>
        <comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
      </block>
      

      Das ergibt diese JavaScript-Funktion:
      4cab7414-5ae2-45d9-b7f5-06036e266f7d-image.png
      mit folgenden Quelltext:

      sendTo('puppeteer.0', 'screenshot', { 
          url: urlOfVISView,                      /* URL der Webseite aus welcher der Screenshot erstellt werden soll */
          path: imageSaveToFilenameWithPath,      /* Dateiname und Pfad für lokales Speichern des Bildes */
          width: viewWidth,                       /* Breite des virtuellen Browserfensters */
          height: viewHeight,                     /* Höhe des virtuellen Browserfensters */
          quality: jpgQuality,                    /* Qualität des JPG, sollte immer 100 sein */
      
          waitOption: {
              waitForSelector: waitForSelector,   /* Wartet for dem Screenshot bis dieses Objekt sichtbar ist */
              waitForTimeout: 10000               /* Timeout, solange wird auf das Objekt gewartet */
          },
      
          fullPage: false,                        /* True würde die ganze Seite Snapshoten und die clip-Anweisungen ignorieren */
      
          clip: {         
              x: cutoutX,                         /* Koordinate X an der der Ausschnitt beginnen soll */
              y: cutoutY,                         /* Koordinate Y an der der Ausschnitt beginnen soll */
              width: cutoutWidth,                 /* Breite des Ausschnittes */
              height: cutoutHeight                /* Höhe des Ausschnittes */
            }
      }, obj => {
            if (obj.error) {
              console.warn("Fehler beim Aufruf der View: " + urlOfVISView + " => " + obj.error.message);
            } else {
                /* in obj.result sind die Binärdaten des Bildes */
              var request = require('request');
              var options = {
                  url: imageUploadURL,
                  method: 'POST',
                  formData: { 
                      "dither": "0", 
                      "mac": ePaperMAC,
                      "image": obj.result },
              };
              /* und die URL aufrufen: */
              request(options,function (error, response, body){
                  //log("Login-Antwort: " + JSON.stringify(response));
                  //console.warn("ImageUpload: " + JSON.stringify(response));
                  console.log("Image:" + imageSaveToFilenameWithPath + " => ImageUploadStatusCode: " + response.statusCode);
                  /*
                  if(parseInt(body.error_code)==0) {
                      // nix
                      } else {
                      // wieder nix
                  }
                  */
              });
            }
      });
      
      

      Nun findet Ihr unten unter Funktionen diese neue mit dem sperrigen Namen ScreenshotVISandUpload2OpenEPaperLink:
      362e64b7-2f3c-4cd0-a7e8-75e8193dfbc2-image.png
      Hui Tiffy, ganz schön viele Parameter! Keine Angst, die gehen wir gleich durch.

      Als nächstes Sammeln wir zwei der notwendigen Parameter und speichern diese in Variablen die ihr selbst neu erstellen müsst:
      348c897c-2825-4a10-a20b-781fa769f1ee-image.png
      Ist die URL eures OpenEPaper AccessPoints, also die IP-Adresse über welche Ihr die Weboberfläche des AccessPoints erreicht. daran hängt Ihr ein /imguplad an. Vollständig also so:

      http://192.168.2.131/imgupload
      

      Die Adresse 192.168.2.131 müsst ihr natürlich an eure anpassen.

      026941d6-0e77-4b02-9540-abd65efd3abb-image.png
      ist die MAC des ePaper-Tags welches den Inhalt erhalten soll. Die MAC findet Ihr auf der Weboberfläche des Tags. Aus dem Dialog wenn man es anklickt kann man diese oben links markieren und kopieren:
      db4c430b-7904-4b5a-ba3c-96ed9fd90743-image.png

      Denn Rest habe ich hier bei mir direkt eingetragen, AP und Tag habe ich Variablen gespeichert weil ich diese so schnell ändern kann ohne das ganze Skript durchgehen zu müssen.

      Unter die beiden Variablen setzen wir nun den JavaScript Block und befüllen die Parameter:
      c9ef1fc3-0976-4dd3-9f68-7b9b0a725fac-image.png

      Die Parameter im Detail:
      410580eb-6bc7-4342-852a-93548f80215b-image.png
      Ist der Dateiname und Pfad wohin er den Screenshot speichert. Das muss also ein Ort sein wohin der Benutzer iobroker schreiben darf. /tmp/ sollte immer gehen. Eigentlich brauchen wir die Datei gar nicht, aber Puppeteer will die nun mal speichern. Andere hatten hier in den Beispielen gar keinen Pfad angegeben, das klappt bei mir nicht, dann versucht er das Bild im Ordner von Puppeteer zu speichern was nicht erlaubt ist.
      Gebt hier also Pfad mit Dateinamen an, die Endung muss .jpg sein:

      /tmp/briefkasten-test.jpg
      

      2ab5781f-5028-4ba5-8123-d8824a64c2b0-image.png
      Ist die URL zu eurer View - am besten oben aus der Adressleiste kopieren und hier einfügen

      2005a335-e8ae-4801-92a2-bcd17dbd01f5-image.png

      Puppeteer ruft die Seite auf wie ein Webbrowser das machen würde. Die Angabe ist wie groß das Browserfenster ist. Hier also 800x600 Pixel. Beachtet das die Felder Blau sind - das ist der Zahlenblock aus dem Mathematik-Menü

      c7090b32-48dc-4607-9a2a-f4d2ab3ddea7-image.png
      Das ist die Größe des Ausschnitts gefolgt von der Qualität.
      Die ersten beiden 0 sind die Koordinaten welche die Ecke oben links beschreiben.
      296 ist die Breite (nach rechts), 128 die Höhe (nach unten). Das ist der Bildausschnitt den er dann speichert.
      Die Qualität sollte immer 100 sein damit das Bild besser aussieht.

      cad18f8f-d309-43c9-8e26-7b03bcc9a22a-image.png

      Dieser Eintrag lässt Puppeteer auf dieses View-Widget warten und erst dann den Screenshot auslösen. So ist sichergestellt das die Seite fertig geladen ist, er aber auch nicht unnötig lange wartet bis er weiter macht. Diese Widget-Id könnt Ihr auf 2 Arten bekommen:
      Ihr geht in den VIS-Editor auf die betreffen View und lasst euch die Liste mit den Widgets anzeigen:
      2404c402-74ed-49f4-bdac-31ced9dff6a4-image.png
      Nehmt im Zweifel das Widget mit der höchsten Nummer. Oder das was auf jeden Fall zu sehen sein muss.

      Die andere Methode ist das Ihr euch den Quelltext der Webseite mit der View anzeigen lasst (die nach Play erscheint) und dort die #w00.... Einträge durchgeht. Im Quelltext könnt Ihr ja auch suchen und so im Zweifel prüfen ob das Widget dabei ist.

      ed4ff489-8ca7-4c64-be87-55684135b9bb-image.png
      Sind unsere beiden Varibalen mit der URL und der MAC.

      Wenn Ihr das Skript speichert und ausführt - und es keine Fehler gibt - sollte er einmalig das Bild auf den ePaper Tag schieben.
      d3cf67b9-c72e-43ed-8213-d79181e96b29-image.png

      Unten und im Log sollte eine Info-Meldung erscheinen wenn er das Bild am AccessPoint abgeliefert hat.

      Schritt 4:
      Wenn das funktioniert könnt Ihr das Skript erweitern so das er immer sendet wenn sich in eurer VIS etwas geändert hat.
      Beachtet das der AP das Bild immer annehmen wird. Er sendet es aber nur neu an den ePaper Tag wenn das Bild sich auch verändert hat.

      Edit 22.01.2022: Typos + Satzbau

      kopierschnitte created this issue in foxriver76/ioBroker.puppeteer

      closed Installation/Usage not possible on ARM64 platforms (RPi, etc.) #13

      posted in Praktische Anwendungen (Showcase)
      BananaJoe
      BananaJoe
    • RE: [Skript] Absolute Feuchte berechnen

      Ich habe mal eine Version v0.6.6 erstellt.
      Diese kommt ohne das zusätzliche Modul dewpoint aus, man muss dieses als nicht mehr in der JavaScript-Instanz hinzufügen (ich hatte das vorhaben im Quellcode gesehen und dachte das kann doch nicht so schwer sein)

      //
      // Raumklima - v0.6.6
      //
      // Berechnet Taupunkt, absolute Luftfeuchtigkeit, Enthalpie, Lüftungsempfehlung,
      // gemessene Temperatur & Luftfeuchtigkeit inkl. Offset zwecks Kalibrierung
      // -----------------------------------------------------------------------------
      //
      // Formeln zur Berechnung der Luftfeuchtigkeit:
      // http://www.nabu-eibelshausen.de/Rechner/feuchte_luft_enthalpie.html
      //
      // Empfehlung Paul53:
      // Kalibrierung der Offsetwerte in einer für den Vergleich relevanten Umgebung
      // z.B. 22°C, 65% Luftfeuchte (nicht im Winter).
      //
      // gute Infos zum Raumklima:
      // https://www.energie-lexikon.info/luftfeuchtigkeit.html
      // http://www.energiebuero-online.de/bauphysik/richtigluften.htm
       
      // Autoren des Skripts:
      // -----------------------------------------------------------------------------
      // - Paul53:
      //   Formeln, Idee, Experte im Bereich Raumklima, Korrekturen am gr. Skript
      // - Solear:
      //   Zusammenfassung der Skripte/Formeln von Paul53
      // - ruhr70:
      //   Ein Skript für alle vorhandenen Räume
      // - eric 2905:
      //   Optimierungen, viele neue Ideen, JSON-Ausgabe, globale Datenpunkte
      // - Andy3268:
      //   Hinzufügen der 4.ten Bedingung für Raumfeuchte Grenzwerte
      // - BananaJoe:
      //   Verzicht auf externes Modul "dewpoint"
       
      // https://forum.iobroker.net/topic/2313/skript-absolute-feuchte-berechnen/437 
      // TODO:
      // -----------------------------------------------------------------------------
      //
      // - Einstellungen Hysterese (Expertenmodus)
      //
      // - setState / getState, die es nicht gibt: Fehler abfangen und Warnung ausgeben, damit der Adapter sich nicht beendet
      //
      // - Luftdruck alternativ vom Messgerät und nicht über Skript (ggf. per Raum)
      //
      // - Auswählbar: Datenpunkte ohne Einheit (zusätzlich) erzeugen (z.B. für vis justgage, value & indicator)
      //
      // - Auswählbar:
      //   Zweig Raum:    NICHT anlegen
      //   JSON:          NICHT anlegen
      //   DETAILS:       NICHT anlegen
      //   CONTROL:       NICHT anlegen
      //
      // - JSON wird recht groß: ggf. Datenpunkte für JSON auswählbar machen
      //
      // - ggf. JSON nicht als String zusammenbauen, sondern als json-Objekt (dann JSON.stringify(json))
      //
      // - Zähler einbauen: Anzahl Räume in Hysterese (Grenzbereich)
      //
      // # "Lüftungsengine":
      // -------------------
      // - möglichst an die individuellen Situationen und Vorlieben anpassbar
      // - differenziertere Lüftungsempfehlung
      // - CO2, Luftgüte einbeziehen
      // - Experteneinstellungen (welche Werte sind einem wichtig)
      // - Modus mit Werten/Prioritäten (wie dringend muss gelüftet werden)
      // - Kellerentlüftung einbauen (Raum markierbar als Keller)
      // - Sommer / Winter (Heizperiode) berücksichtigen
      // - dringend lüften, ab 70% rel. Luftfeuchtigkeit und geeigneter Außenluft (Vergl. absolute Luftfeuchtigkeit)
      // - Massnahme: zu trockene Luft (rel. Luftfeuchtigkeit < 40%)
      // - Massnahme: Luft rel. Feuch > 60% oder 65% (?)
      // - Feuchtigkeitstrend berücksichtigen. Ist ie Tendenz fallend, Bedingung "Entfeuchten" überstimmen.
       
      // Ideensammlung Lüftungsengine
      // - zentraler Datenpunkt: Heizperiode
      // - je Raum eine opt. Datenpunkt für eine zugeordnete Heizung (Zieltemperatur und Heizung an/aus)
      // - je Raum die Wunschtemperatur
      // - Prio: schlechte Luftqualität
      // - Prio: kühlen, wenn Temperaturunterschied zu groß
      // - Prio: zu trockene Luft (rel.)
      // - Prio: zu feuchte Luft (rel.)
       
      // berücksichtigen / Beobachtungen:
      //
      // wenn draussen zu kalt ist, macht das lüften tlw. keinen Sinn mehr
      // wenn die Zimmertemperatur bis zum Minimum abkühlt kann torz Unterschid xi/xa
      // xi und die rel. Luftfeuchte weiter steigen, da die dann kältere Raumluft weniger 
      // Luftfeuchtigkeittragen kann.
       
      // -----------------------------------------------------------------------------
      // Einstellungen Skriptverhalten, eigene Parameter -  !! bitte anpassen !!
      // -----------------------------------------------------------------------------
       
      // Wichtig:                             // betrifft den CONTROL Zweig bei den Raumdatepunkten (Offsets, Raummindestemperatur (Auskühlschutz))
      var skriptConf  = true;                 // Anwender kann sich aussuchen, ob er die Werte im Skript oder über die Objekte pflegen möchte
                                             // true:  Raumwerte werden über das Skript geändert/überschrieben (var raeume)
                                             // false: Raumwerte werden über Objekte (z.B. im Admin, Zustände oder VIS) geändert
       
      var debug = false;                      // true: erweitertes Logging einschalten
       
       
      // eigene Parameter:
      var hunn            = 15;           // eigene Höhe über nn (normalnull), z.B. über http://de.mygeoposition.com zu ermitteln
      var defaultTemp     = 18.00;     // Default TEMP_Minimum, wenn im Raum nicht angegeben (Auskühlschutz, tiefer soll eine Raumtemperatur durchs lüften nicht sinken)
      var defaultMinFeu   = 40.00;     // Default Mindest Feuchte wenn nicht angegeben.
      var defaultMaxFeu   = 60.00;     // Default Maximal Feuchte wenn nicht angegeben.
       
      var cronStr         = "*/30 * * * *";       // Zeit, in der alle Räume aktualisiert werden (da auf Änderung der Sensoren aktualisiert wird, kann die Zeit sehr hoch sein)
      var strDatum        = "DD-MM-JJJJ SS:mm:ss";// Format, in dem das Aktualisierungsdatum für das JSON ausgegeben wird
       
       
       
      // ### Experteneinstellungen ###
       
      // Lüftungsengine
       
      var hysMinTemp      = 0.5;              // Default 0.5, Hysterese Mindesttemperatur (Auskühlschutz). Innerhalb dieser Deltatemperatur bleibt die alte Lüftungsempfehlung für den Auskühlschutz bestehen.
      var hysEntfeuchten  = 0.2;              // Default 0.3, Hysterese Entfeuhten: Delta g/kG absolute Luftfeuchte. In dem Delta findet keine Änderung der alten Lüftungsempfehlung statt    
       
       
      // Skriptverhalten
      var delayRooms      = 500;              // Zeit in ms als Verzögerung, wie die Räume abgearbeitet werden
       
       
      // Pfade für die Datenpunkte:
      var pfad        = "Raumklima"   +".";   // Pfad unter dem die Datenpunkte in der Javascript-Instanz angelegt werden
       
      // Unterpfade unterhalb des Hauptpfads
      var raumPfad    = "Raum"        +".";   // Pfad unterhalb des Hauptpfads für die Räume
      var controlPfad = "CONTROL"     +".";   // Pfad innerhalb des Raums für Kontrollparameter
      var detailPfad  = "DETAILS"     +".";   // Pfad innerhalb des Raums für Detailparameter ("" und ohne ".", wenn kein Detailpfad gewünscht)
      var detailEnginePfad = "DETAILS_Lüftungsempfehlung" + "."; // Pfad innerhalb des Raums für Detailparameter zur Lüftungsengine
       
      var infoPfad    = "Skriptinfos" +".";   // Pfad für globale Skriptparameter zur Info
       
       
      // -----------------------------------------------------------------------------
      // Räume mit Sensoren, Parametrisierung -           !! bitte anpassen !!
      // -----------------------------------------------------------------------------
       
      // jeder Sensor darf nur einmal verwendet werden!
       
      // wird kein Aussensensor angegeben, wird der Sensor als Aussensensor behandelt!
       
      var raeume = { // Keine Leerzeichen (Name wird als Datenpunktname verwendet!)
          // Sensoren Aussen
          "TF_Haustuer" : {
              "Sensor_TEMP"           :   'zigbee.0.xxx1.temperature'/*Aussensensor Haustuer Temperatur */,
              "Sensor_HUM"            :   'zigbee.0.xxx1.humidity'/*Aussensensor Haustuer Feuchtigkeit */,
              "Sensor_TEMP_OFFSET"    :   0.0,
              "Sensor_HUM_OFFSET"     :   0
                     
          },
          "TF_Terrasse" : {
              "Sensor_TEMP"           :   'zigbee.0.xxx2.temperature'/*Aussensensor Garten Temperatur */,
              "Sensor_HUM"            :   'zigbee.0.xxx2.humidity'/*Aussensensor Garten Feuchtigkeit */,
              "Sensor_TEMP_OFFSET"    :   0.0,
              "Sensor_HUM_OFFSET"     :   0
                     
          },
          // Sensoren Innen
          "Badezimmer" : {
              "Sensor_TEMP"           :   'zigbee.0.xxx3.temperature'/* Sensor Badezimmer Temperatur */,
              "Sensor_HUM"            :   'zigbee.0.xxx3.humidity'/* Sensor Badezimmer Feuchtigkeit */,
              "Sensor_TEMP_OFFSET"    :   0.0,
              "Sensor_HUM_OFFSET"     :   0,
              "TEMP_Minimum"          :   defaultTemp, // oder Zieltemperatur in Form von: 20.00 angeben
              "Aussensensor"          :   "TF_Haustuer"
          },
          "Schlafzimmer" : {
              "Sensor_TEMP"           :   'zigbee.0.xxx4.temperature',
              "Sensor_HUM"            :   'zigbee.0.xxx4.humidity',
              "Sensor_TEMP_OFFSET"    :   0.0,
              "Sensor_HUM_OFFSET"     :   0,
              "TEMP_Minimum"          :   defaultTemp, // oder Zieltemperatur in Form von: 20.00 angeben
              "Aussensensor"          :   "TF_Terrasse"
          },
          "Wohnzimmer" : {
              "Sensor_TEMP"           :   'zigbee.0.xxx5.temperature',
              "Sensor_HUM"            :   'zigbee.0.xxx5.humidity',
              "Sensor_TEMP_OFFSET"    :   0.0,
              "Sensor_HUM_OFFSET"     :   0,
              "TEMP_Minimum"          :   defaultTemp, // oder Zieltemperatur in Form von: 20.00 angeben
              "Aussensensor"          :   "TF_Terrasse"
          },
         
      };
       
       
      // =============================================================================
       
      // =============================================================================
      // Skriptbereich. Ab hier muss nichts mehr eingestellt / verändert werden.
      // =============================================================================
       
      // =============================================================================
      
      // Das Modul dewpoint - integriert, keine externe Abhängigkeit mehr
      // Start Modul Dewpoint
      // Calculation of absolute humidity x (in g water per kg dry air) and of dew point temperature (in �C)
      var dewpoint = function(h) {
              var z = 1.0 - (0.0065 / 288.15) * h;
              // air pressure in hPa
              this.p = 1013.25 * Math.pow(z, 5.255);
              this.A = 6.112;
              }
      dewpoint.prototype.Calc = function(t, rh) {
              t = parseFloat(t);
              var m = 17.62;
              var Tn = 243.12;
              if (t < 0.0) {
              m = 22.46;
              Tn = 272.62;
              }
       
              var     sd = this.A * Math.exp(m * t / (Tn + t));
              var d = sd * rh / 100.0;
       
          return {
             x: 621.98 * d /(this.p - d),
             dp: Tn * Math.log(d/this.A) / (m - Math.log(d/this.A))
              };
      };
      // Ende Modul Dewpoint
      
       
      var idSkriptinfoBar         = pfad + infoPfad + "Luftdruck";
      var idSkriptinfoHunn        = pfad + infoPfad + "Höhe_über_NN";
       
      // forceCreation = true, damit bei geändert eigener Höhe im Konfigurationsbereich der Datenpunkt neu geschrieben wird
      createState(idSkriptinfoBar, luftdruck(hunn), true, {
         name: 'mittlerer Luftdruck in bar',
         desc: 'mittlerer Luftdruck in bar, errechnet anhand der eigenen Höhe über NN',
         type: 'number',
         unit: 'bar',
         role: 'info'
      });
       
      createState(idSkriptinfoHunn, hunn, true, {
         name: 'Eigene Höhe über NN',
         desc: 'Eigene Höhe über NN (Normal Null), als Basis für den mittleren Luftdruck',
         type: 'number',
         unit: 'm',
         role: 'info'
      });
       
       
      var raumDatenpunkte = {
         "x" : {
             "DpName" : "Feuchtegehalt_Absolut",
             "init": 0,
             "dp": {
                 "name": 'absoluter Feuchtegehalt',
                 "desc": 'absoluter Feuchtegehalt, errechnet',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'g/kg'
             }
         },
         "rh" : {
             "DpName" : "relative_Luftfeuchtigkeit",
             "init": 0,
             "dp": {
                 "name": 'gemessene relative Luftfeuchtigkeit (inkl. Offset)',
                 "desc": 'relative Luftfeuchtigkeit, vom Sensor + Offset zum Ausgleich von Messungenauigkeiten des Geräts',
                 "type": 'number',
                 "role": 'value',
                 "unit": '%'
             }
         },
         "dp" : {
             "DpName" : "Taupunkt",
             "init": 0,
             "dp": {
                 "name": 'Taupunkt',
                 "desc": 'Taupunkt. Temperatur von Wänden, Fenstern, usw. ab der sich die Feuchtigkeit niederschlägt.',
                 "type": 'number',
                 "role": 'value',
                 "unit": '°C'
             }
         },
         "t" : {
             "DpName" : "Temperatur",
             "init": 0,
             "dp": {
                 "name": 'gemessene Temperatur (inkl. Offset)',
                 "desc": 'gemessene Temperatur vom Sensor zzgl. eines Offsets um Geräteungenauigkeiten auszugleichen',
                 "type": 'number',
                 "role": 'value',
                 "unit": '°C'
             }
         },
         "h" : {
             "DpName" : detailPfad + "Enthalpie",
             "init": 0,
             "dp": {
                 "name": 'Enthalpie',
                 "desc": 'Enthalpie',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'kJ/kg'
             }
         },
         "sdd" : {
             "DpName" : detailPfad +"Sättigungsdampfdruck",
             "init": 0,
             "dp": {
                 "name": 'Sättigungsdampfdruck',
                 "desc": 'Sättigungsdampfdruck',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'hPa'
             }
         },
         "dd" : {
             "DpName" : detailPfad + "Dampfdruck",
             "init": 0,
             "dp": {
                 "name": 'Dampfdruck',
                 "desc": 'Dampfdruck',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'hPa'
             }
         },
         "rd" : {
             "DpName" : "Dampfgewicht",
             "init": 0,
             "dp": {
                 "name": 'Dampfgewicht (Wassergehalt)',
                 "desc": 'Dampfgewicht (Wassergehalt)',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'g/m³'
             }
         },
         "maxrd" : {
             "DpName" : detailPfad + "Dampfgewicht_maximal",
             "init": 0,
             "dp": {
                 "name": 'max. Dampfgewicht (Wassergehalt)',
                 "desc": 'max. Dampfgewicht (Wassergehalt) bei aktueller Temperatur',
                 "type": 'number',
                 "role": 'value',
                 "unit": 'g/m³'
             }
         },
         "lüften" : {
             "DpName" : "Lüftungsempfehlung",
             //"init": false,
             "dp": {
                 "name": 'Lüftungsempfehlung',
                 "desc": 'Lüftungsempfehlung',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_b1" : {
             "DpName" : detailEnginePfad + "Lüften_b1_Entfeuchten",
             //"init": false,
             "dp": {
                 "name": 'Lüften Bedingung 1 entfeuchten',
                 "desc": 'Lüften Bedingung 1 entfeuchten erfüllt',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_b2" : {
             "DpName" : detailEnginePfad + "Lüften_b2_Kühlen",
             //"init": false,
             "dp": {
                 "name": 'Lüften Bedingung 2 kühlen',
                 "desc": 'Lüften Bedingung 2 kühlen erfüllt',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_b3" : {
             "DpName" : detailEnginePfad + "Lüften_b3_Auskühlschutz",
             //"init": false,
             "dp": {
                 "name": 'Lüften Bedingung 3 Auskühlschutz',
                 "desc": 'Lüften Bedingung 3 Auskühlschutz erfüllt (Innentemperatur soll nicht unter Minimumteperatur fallen)',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_b4" : {
             "DpName" : detailEnginePfad + "Lüften_b4_Raumfeuchte",
             //"init": false,
             "dp": {
                 "name": 'Lüften Bedingung 4 Raumfeuchte',
                 "desc": 'Lüften Bedingung 4 Raumfeuchte erfüllt',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_Hysterese" : {
             "DpName" : detailEnginePfad + "Lüften_Hysterese",
             //"init": false,
             "dp": {
                 "name": 'Logik im Bereich der Hysterese. Keine Änderung der bestehenden Lüftungsempfehlung.',
                 "desc": 'Logik im Bereich der Hysterese. Keine Änderung der bestehenden Lüftungsempfehlung.',
                 "type": 'boolean',
                 "role": 'value'
             }
         },
         "lüften_Beschreibung" : {
             "DpName" : detailEnginePfad + "Lüftungsempfehlung_Beschreibung",
             "init": "",
             "dp": {
                 "name": 'Lüftungsempfehlung beschreibender Text',
                 "desc": 'Lüftungsempfehlung beschreibender Text',
                 "type": 'string',
                 "role": 'value'
             }
         }
      };
       
         // #1 - Entfeuchten:    Außenluft ist mind. (hysEntfeuchten + 0,1) trockener als Innen
         // #2 - Kühlen:         Außentemperatur ist mindestens 0,6 Grad kühler als innen TODO: im Winter auch?
         // #3 - Auskühlschutz:  Innentemperatur ist höher als die Mindesttemperatur
       
       
      var raumControl = {
         "Sensor_TEMP_OFFSET" : {
             "DpName" : "Sensor_TEMP_OFFSET",
             "init": 0,
             "dp": {
                 "name": 'Offset Temperatur zum Sensormesswert (Ausgleich von Ungenauigkeiten)',
                 "desc": 'Offset Temperatur zum Sensormesswert (Ausgleich von Ungenauigkeiten)',
                 "type": 'number',
                 "role": 'control.value',
                 "unit": '°C'
             }
         },
         "Sensor_HUM_OFFSET" : {
             "DpName" : "Sensor_HUM_OFFSET",
             "init": 0,
             "dp": {
                 "name": 'Offset Luftfeuchtigkeit zum Sensormesswert (Ausgleich von Ungenauigkeiten)',
                 "desc": 'Offset Luftfeuchtigkeit zum Sensormesswert (Ausgleich von Ungenauigkeiten)',
                 "type": 'number',
                 "role": 'control.value',
                 "unit": '%'
             }
         },
         "TEMP_Minimum" : {
             "DpName" : "TEMP_Minimum",
             "init": 0,
             "dp": {
                 "name": 'Auskühlschutz Mindestraumtemperatur',
                 "desc": 'Auskühlschutz Mindestraumtemperatur zum lüften',
                 "type": 'number',
                 "role": 'control.value',
                 "unit": '°C'
             }
         },
         "FEUCH_Minimum" : {
             "DpName" : "FEUCH_Minimum",
             "init": 0,
             "dp": {
                 "name": 'Mindest Rel. Raumfeuchte',
                 "desc": 'Mindest Rel. Raumfeuchte zum lüften',
                 "type": 'number',
                 "role": 'control.value',
                 "unit": '%'
             }
         },
         "FEUCH_Maximum" : {
             "DpName" : "FEUCH_Maximum",
             "init": 0,
             "dp": {
                 "name": 'Maximal Rel. Raumfeuchte ',
                 "desc": 'Maximal Rel. Raumfeuchte zum lüften',
                 "type": 'number',
                 "role": 'control.value',
                 "unit": '%'
             }
         },
         "Aussensensor" : {
             "DpName" : "Aussensensor",
             "init": "",
             "dp": {
                 "name": 'Aussensensor, der zum Vergleich genommen wird',
                 "desc": 'Aussensensor, der zum Vergleich genommen wird',
                 "type": 'string',
                 "role": 'control.value'
             }
         }
      };
       
       
      // globale Skript-Variablen/Objekte
      //------------------------------------------------------------------------------
       
      var xdp     = new dewpoint(hunn);
       
      var pbar    = luftdruck(hunn);          // individueller Luftdruck      in bar (eigene Höhe)
       
       
       
      //------------------------------------------------------------------------------
      // Funktionen
      //------------------------------------------------------------------------------
       
      function writeJson(json) {
         return JSON.stringify(json);
      }
       
       
      // prüft ob setObjects() für die Instanz zur Verfügung steht (true/false)
      function checkEnableSetObject() { 
         var enableSetObject = getObject("system.adapter.javascript." + instance).native.enableSetObject;
         return enableSetObject;
      }
       
       
      function setChannelName(channelId,channelName){
         if(checkEnableSetObject()) { // wenn setObject nicht in der Instanz freigeschaltet ist, wird der Channel nicht angelegt
         // CHANNEL anlegen
             setObject("javascript." + instance + "." + channelId, {
                 common: {
                     name: channelName
                 },
                 type: 'channel'
             }, function(err) {
                 if (err) logs('Cannot write object: ' + err,"error");
             });
         }
      }
       
       
      function lueftenDp(datenpunktID) {
         return (datenpunktID == "lüften") || (datenpunktID == "lüften_Beschreibung") || (datenpunktID == "lüften_b1") || (datenpunktID == "lüften_b2") || (datenpunktID == "lüften_b3") || (datenpunktID == "lüften_b4") || (datenpunktID ==  "lüften_Hysterese");
      }
       
       
      function createDp() {
         var name;
         var init;
         var forceCreation;
         var common;
         for (var raum in raeume) {
             for (var datenpunktID in raumDatenpunkte) {
                 name = pfad + raumPfad + raum + "." + raumDatenpunkte[datenpunktID].DpName;
                 init = raumDatenpunkte[datenpunktID].init;
                 forceCreation = false; // Init der Datenpunkte wird nur beim ersten Star angelegt. Danach bleiben die Wert auch nach Skritpstart enthalten.
                 common = raumDatenpunkte[datenpunktID].dp;
                 
                 if (lueftenDp(datenpunktID)) {
                     if (!raeume[raum].Aussensensor) {
                         if (datenpunktID == "lüften") {
                             log(raum + ": kein Aussensensor angegeben.  ### Messpunkte werden als Aussensensoren behandelt. ###","info"); // Warnung ist im Log OK, wenn es sich um einen Außensensor handelt.
                             setChannelName(pfad + raumPfad + raum,"Aussensensor");
                         }
                     } else {
                         createState(name, init , forceCreation, common);
                         if (debug) log("neuer Datenpunkt: " + name);
                     }
                 } else {
                     createState(name, init , forceCreation, common);
                     if (debug) log("neuer Datenpunkt: " + name);
                 }
                 
             }
             for (var control in raumControl) {
                 name = pfad + raumPfad + raum + "." + controlPfad + raumControl[control].DpName;
                 //init = raumControl[control].init;
                 forceCreation = skriptConf;
                 common = raumControl[control].dp;
                 if (typeof raeume[raum][raumControl[control].DpName] !=="undefined") {
                     init = raeume[raum][raumControl[control].DpName];
                     createState(name, init , forceCreation, common);
                     var channelname = "Nur Info. Werte aus dem Skript zählen. Kann im Skript umgestellt werden.";
                     if (!skriptConf) channelname = "Änderungen hier in den Objekten werden berechnet";
                     setChannelName(pfad + raumPfad + raum + "." + controlPfad.substr(0, controlPfad.length-1),channelname);
                 }
             }
         }
         
         //eric2905 Datenpunkt "Lüften" erzeugen
         // -------------------------------------------------------------------------
         createState(pfad + 'Lüften', false, {
          name: 'Muss irgendwo gelüftet werden',
          desc: 'Muss irgendwo gelüftet werden',
          type: 'boolean',
          unit: '',
          role: 'value'
         });
       
         createState(pfad + 'Lüften_Liste', "[]", {
          name: 'Liste der Räume in denen gelüftet werden muss',
          desc: 'Liste der Räume in denen gelüftet werden muss',
          type: 'string',
          unit: '',
          role: 'value'
         });
       
         // eric2905 Ende -----------------------------------------------------------
       
         //eric2905 Datenpunkt "JSON" erzeugen
         // -------------------------------------------------------------------------
         createState(pfad + 'JSON', "", {
          name: 'JSON-Ausgabe aller Werte',
          desc: 'JSON-Ausgabe aller Werte',
          type: 'string',
          unit: '',
          role: 'value'
         });
         // eric2905 Ende -----------------------------------------------------------
       
         //eric2905 Datenpunkt "Aktualsierung" erzeugen
         // -------------------------------------------------------------------------
         createState(pfad + 'Aktualsierung', "", {
          name: 'Aktualisierungszeitpunkt der JSON-Ausgabe',
          desc: 'Aktualisierungszeitpunkt der JSON-Ausgabe',
          type: 'string',
          unit: '',
          role: 'value'
         });
         // eric2905 Ende -----------------------------------------------------------
        
        
         //eric2905 Datenpunkt "countLueften" erzeugen
         // -------------------------------------------------------------------------
         createState(pfad + 'Lüften_Anzahl', 0, {
          name: 'Anzahl Lüftungsempfehlungen',
          desc: 'Anzahl Lüftungsempfehlungen',
          type: 'number',
          unit: '',
          role: 'value'
         });
         // eric2905 Ende -----------------------------------------------------------
       
         log("Datenpunkte angelegt");
      }
       
       
      // rundet einen Float auf eine bestimmte Anzahl Nachkommastellen
      function runden(wert,stellen) {
         return Math.round(wert * Math.pow(10,stellen)) / Math.pow(10,stellen);
      }
       
      // berechnet den mittleren Luftdruck für eine Höhenangabe in NN 
      function luftdruck(hunn) {
         var pnn         = 1013.25;                                  // Mittlerer Luftdruck          in hPa bei NN
         var p           = pnn - (hunn / 8.0);                       // individueller Luftdruck      in hPa (eigenen Höhe)
         return p / 1000;                                            // Luftdruck von hPa in bar umrechnen
      }
       
      // Color Boolean (farbige Ausgabe Boolean als String, z.B. für das Log)
      function cob(boolean) { 
         var cobStr = (boolean) ? '<span style="color:lime;"><b>true</b></span>' : '<span style="color:red;"><b>false</b></span>';
         return cobStr;
      }
       
      function makeNumber(wert) {
         if(isNaN(wert)) {
             wert = parseFloat(wert.match(/\d+[.|,]?\d+/g));
         }
         return wert;
      }
       
       
       
      // Berechnungen Luftwerte 
      // ----------------------
       
      function calcSaettigungsdampfdruck(t) {    // benötigt die aktuelle Temperatur
         // Quelle: http://www.wetterochs.de/wetter/feuchte.html#f1
         var sdd,a,b;
         a = 7.5;
         b = 237.3;
         sdd = 6.1078 * Math.pow(10,((a*t)/(b+t)));
         return sdd; // ssd = Sättigungsdampfdruck in hPa
      }
       
      function calcDampfdruck(sdd,r) {
         // Quelle: http://www.wetterochs.de/wetter/feuchte.html#f1
         var dd = r/100 *sdd;
         return dd;  // dd = Dampfdruck in hPa
      }
       
      function calcTemperaturKelvin(t) {
         var tk = t + 273.15;
         return tk;
      }
       
      function calcDampfgewicht(dd,t) { // Wassergehalt
         // Dampfgewicht rd oder AF(r,TK) = 10^5 * mw/R* * DD(r,T)/TK
         // Quelle: http://www.wetterochs.de/wetter/feuchte.html#f1
         var tk = calcTemperaturKelvin(t);
         var mw = 18.016; // kg/kmol (Molekulargewicht des Wasserdampfes)
         var R  = 8314.3; // J/(kmol*K) (universelle Gaskonstante)
         var rd = Math.pow(10,5) * mw/R * dd/tk; 
         return rd; // rd = Dampfgewicht in g/m^3
      }
       
      function calcMaxDampfgewicht(rd,r) {
         var maxrd = rd / r *100;
         return maxrd;
      }
       
       
       
       
      // Berechnung: alle Werte je Raum
      // -------------------------------
       
       
      function calc(raum) {                                           // Über Modul Dewpoint absolute Feuchte berechnen
       
         var t           = getState(raeume[raum].Sensor_TEMP).val;   // Temperatur auslesen
         var rh          = getState(raeume[raum].Sensor_HUM).val;    // Feuchtigkeit relativ auslesen
       
         t   = makeNumber(t);                                        // Temperatur in Number umwandeln
         rh  = makeNumber(rh);                                       // relative Luftfeuchtigkeit in Number umwandeln
       
         var toffset     = 0.0;                                      // Default Offset in °C
         var rhoffset    = 0;                                        // Default Offset in %
         if(typeof raeume[raum].Sensor_TEMP_OFFSET !=="undefined") {
             // Temperatur, wenn ein Offset vorhanden ist, diesen auslesen und Default überschreiben
             var idtoffset = pfad + raumPfad+ raum + "." + controlPfad + "Sensor_TEMP_OFFSET";
             toffset = getState(idtoffset).val;  // Offset aus den Objekten/Datenpunkt auslesen
         }
         if(typeof raeume[raum].Sensor_HUM_OFFSET !=="undefined") {
             // Luftfeuchtigkeit, wenn ein Offset vorhanden ist, diesen auslesen und Default überschreiben
             var idrhoffset = pfad + raumPfad + raum + "." + controlPfad + "Sensor_HUM_OFFSET";
             rhoffset = getState(idrhoffset).val;  // Offset aus den Objekten/Datenpunkt auslesen
         }
       
         t       = t     + toffset;      // Messwertanpassung: gemessene Temperatur um den Offset ergänzen
         rh      = rh    + rhoffset;     // Messwertanpassung: gemessene relative Luftfeuchtigkeit um Offset ergänzen
       
         var y           = xdp.Calc(t, rh);
         var x   = y.x;  // Zu errechnende Variable für Feuchtegehalt in g/kg
         var dp  = y.dp; // Zu errechnende Variable für Taupunkt in °C
       
         var h       = 1.00545 * t + (2.500827 + 0.00185894 * t) * x;    // Enthalpie in kJ/kg berechnen
       
         var sdd     = calcSaettigungsdampfdruck(t);                     // Sättigungsdampfdruck in hPa
         var dd      = calcDampfdruck(sdd,rh);                           // dd = Dampfdruck in hPa
         var rd      = calcDampfgewicht(dd,t);                           // rd = Dampfgewicht/Wassergehalt in g/m^3
         var maxrd   = calcMaxDampfgewicht(rd,rh);                       // maximales Dampfgewicht in g/m^3
         
       
         var idx     = pfad + raumPfad + raum + "." + raumDatenpunkte["x"].DpName;   // DP-ID absolute Luftfeuchte in g/kg
         var iddp    = pfad + raumPfad + raum + "." + raumDatenpunkte["dp"].DpName;  // DP-ID Taupunkt in °C
         var idt     = pfad + raumPfad + raum + "." + raumDatenpunkte["t"].DpName;   // DP-ID Temperatur inkl. Offset
         var idrh    = pfad + raumPfad + raum + "." + raumDatenpunkte["rh"].DpName;  // DP-ID relative Luftfeuhtigkeit inkl. Offset
         var ih      = pfad + raumPfad + raum + "." + raumDatenpunkte["h"].DpName;   // DP-ID Enthalpie in kJ/kg
         var isdd    = pfad + raumPfad + raum + "." + raumDatenpunkte["sdd"].DpName;
         var idd     = pfad + raumPfad + raum + "." + raumDatenpunkte["dd"].DpName;
         var ird     = pfad + raumPfad + raum + "." + raumDatenpunkte["rd"].DpName;
         var imaxrd  = pfad + raumPfad + raum + "." + raumDatenpunkte["maxrd"].DpName;
       
       
         setState(idx    , runden(x,2));     // errechnete absolute Feuchte in Datenpunkt schreiben
         setState(iddp   , runden(dp,1));    // errechneter Taupunkt in Datenpunkt schreiben
         setState(idt    , parseFloat(t));   // Sensor Temperatur        inkl. Offset
         setState(idrh   , parseFloat(rh));   // Sensor Relative Feuchte  inkl. Offset
         setState(ih     , runden(h,2));     // Enthalpie in kJ/kg
         setState(isdd   , runden(sdd,2));
         setState(idd    , runden(dd,2));
         setState(ird    , runden(rd,2));
         setState(imaxrd , runden(maxrd,2));
       
       
         // Logik-Engine: Lüftungsempfehlung berechnen
         // -------------------------------------------------------------------------
         if (!raeume[raum].Aussensensor) {
             // kein Aussensensor, keine Lüftungsempfehlung
             if (debug) log("<b>------ " + raum + " ------- Aussen, keine Lüftungsempfehlung -----------</b>");
             return; 
         }
         
         var aussen;
         var idta, idxa;
         if(typeof raeume[raum].Aussensensor !=="undefined") {
             aussen = raeume[raum].Aussensensor; // aussen = "Raumname" des zugehörigen Aussensensors
             idta = pfad + raumPfad + aussen + "." + raumDatenpunkte["t"].DpName;    // DP-ID zugehöriger Aussensensor, Temperatur aussen
             idxa = pfad + raumPfad + aussen + "." + raumDatenpunkte["x"].DpName;    // DP-ID zugehöriger Aussensensor, Luftfeuchtigkeit aussen
         } else {
             return; // wenn es keinen zugehörigen Aussensensor gibt, Funktion beenden (dann muss kein Vergleich berechnet werden)
         }
       
         var ti = t;                     // Raumtemperatur in °C
         var xi = runden(x,2);           // Raumfeuchtegehalt in g/kg
         var ta = getState(idta).val;    // Aussentemperatur in °C
         var xa = getState(idxa).val;    // Aussenfeuchtegehalt in g/kg
         if (xa == 0) return;            // TODO: warum? hatte ich leider nciht dokumentiert (ruhr70)
       
         var mi = defaultTemp;           // Temperaturmindestwert auf Default (Auskühlschutz)
         var xh = defaultMaxFeu;         // Feuchtemaximalwert auf Default
         var xt = defaultMinFeu;         // Feuchteminimalwert auf Default
      	  
         //if(typeof raeume[raum].TEMP_Minimum !=="undefined") {
         if(typeof raeume[raum].TEMP_Minimum == "number") {
             mi = raeume[raum].TEMP_Minimum;
         }
         if(typeof raeume[raum].FEUCH_Maximum == "number") {
             xh = raeume[raum].FEUCH_Maximum;
         }
       
         if(typeof raeume[raum].FEUCH_Minimum == "number") {
             xt = raeume[raum].FEUCH_Minimum;
         }
         
         // Auskühlschutz,  hysMinTemp (Variable) Grad hysMinTemp Hysterese. Tiefer darf die Innentemperatur nicht sinken
         var mih = mi + hysMinTemp;      // Temperaturmindestwert hoch (Mindesttemperatur plus Hysterese)
         var mit = mi;                   // Temperaturmindestwert tief
       
         var idLueften       = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften"].DpName;
         var idLueftenText   = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_Beschreibung"].DpName;
         var idLueftenB1     = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_b1"].DpName;
         var idLueftenB2     = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_b2"].DpName;
         var idLueftenB3     = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_b3"].DpName;
         var idLueftenB4     = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_b4"].DpName;
         var idLueftenHys    = pfad + raumPfad + raum + "." + raumDatenpunkte["lüften_Hysterese"].DpName;
       
         var lueftenText     = "";
       
       
       
         // Lüftungslogik
         // -------------
         // Lüftungsempfehlung steuern mit 0,3 g/kg und 0,5 K Hysterese
         // Bedigungen fürs lüften
         var b1lp = (xa <= (xi - (hysEntfeuchten + 0.1)))    ? true : false;   // Bedingnung 1 lüften positv (Außenluft ist mind. 0,4 trockener als Innen)
         var b2lp = (ta <= (ti - 0.6))                       ? true : false;   // Bedingnung 2 lüften positv (Außentemperatur ist mindestens 0,6 Grad kühler als innen)
         var b3lp = (ti >= mih)                              ? true : false;   // Bedingnung 3 lüften positv (Innentemperatur ist höher als die Minimumtemperatur + Hysterese)
         var b4lp = (rh >= xh)                               ? true : false;   // Bedingnung 4 lüften positv (Relative Raumfeuchte ist höher als die Maximalfeuchtewert)
       
         var b1lpText = "Entfeuchten:    Außenluft ist mind. 0,4 trockener als Innen";
         var b2lpText = "Kühlen:         Außentemperatur ist mindestens 0,6 Grad kühler als innen";
         var b3lpText = "Auskühlschutz:  Innentemperatur ist höher als die Mindesttemperatur";
         var b4lpText = "Raumfeuchte:    Raumfeuchte ist höher als der Maximalfeuchte";
       
         setState(idLueftenB1,b1lp);
         setState(idLueftenB2,b2lp);
         setState(idLueftenB3,b3lp);
         setState(idLueftenB4,b4lp);
       
         // Bedingungen gegen das Lüften
         var b1ln = (xa >= (xi - 0.1))   ? true : false;   // Bedingnung 1 lüften negativ (Außenluft ist zu feucht)
         var b2ln = (ta >= (ti - 0.1))   ? true : false;   // Bedingnung 2 lüften negativ (Außentemperatur zu warm)
         var b3ln = (ti <= mit)          ? true : false;   // Bedingnung 3 lüften negativ (Innentemperatur niedriger als Mindesttemperatur)
         var b4ln = (rh <= xt)           ? true : false;   // Bedingnung 4 lüften negativ (Relative Raumfeuchte ist niedriger als die Mindestfeuchte)
       
         var b1lnText = "Entfeuchten:    Außenluft ist zu feucht";
         var b2lnText = "Kühlen:         Außentemperatur zu warm";
         var b3lnText = "Auskühlschutz:  Innentemperatur niedriger als Mindestraumtemperatur";
         var b4lnText = "Raumfeuchte:    Raumfeuchte ist niedriger als der Mindestfeuchte";
       
         
         // Logik:
         //--------------------------------------------------------------------------
         if (b1lp && b2lp && b3lp && b4lp) {
             // Lüftungsempfehlung, alle bedingungenen erfüllt
             lueftenText = "Bedingungen für Entfeuchten, Kühlen und Auskühlschutz erfüllt.";
             setState(idLueften, true);
             setState(idLueftenHys,false);
       
             if (debug) log(raum + ': <span style="color:limegreen;"><b>Lüftungsempfehlung</b></span>');
       
         } else if (b1ln || b2ln || b3ln || b4ln) {
             // Fenster zu. Ein Ausschlusskriterium reicht für die Empfehlung "Fenster zu".
             lueftenText = "Fenster zu:<br>";
             if (b1ln) lueftenText += b1lnText + "<br>";
             if (b2ln) lueftenText += b2lnText + "<br>";
             if (b3ln) lueftenText += b3lnText + "<br>";
      	   if (b4ln) lueftenText += b4lnText + "<br>";
             setState(idLueften, false);
             setState(idLueftenHys,false);
             if (debug) log(raum + ': <span style="color:red;"><b>Empfehlung Fenster zu</b></span>');
         } else {
             // Hysterese. Keine Änderung der bisherigen Empfehlung.
             if (debug) log(raum + ': <span style="color:orange;"><b>im Bereich der Hysterese</b></span> (keine Änderung der Lüftungsempfehlung');
             if (getState(idLueften).val === null) setState(idLueften,false); // noch keine Empfehlung vorhanden, "Fenster zu" empfehlen
             lueftenText = "Hysterese, keine Änderung der Lüftungsempfehlung";
             setState(idLueftenHys,true);
         }
         setState(idLueftenText, lueftenText);
       
       
         /* Erklärung Lüftungslogik (von Paul53)
            Ergänzung #4 (von Andy3268)
         Lüften:
         wenn    abs. Aussenfeuchte  <   abs. Innenfeuchte     - Hysterese (Entfeuchten)
         UND     Aussentemperatur    <   Innentemperatur       - Hysterese (Kühlen)
         UND     Innentemperatur     >=  Raumtemperaturminimum + Hysterese (Auskühlschutz)
         UND     Innenfeuchte        >=  Raummaximalfechte
         */
       
         // lüften (und - Alle Bedingungen müssen erfüllt sein):
         // #1 - Entfeuchten:    Außenluft ist mind. (hysEntfeuchten + 0,1) trockener als Innen
         // #2 - Kühlen:         Außentemperatur ist mindestens 0,6 Grad kühler als innen TODO: im Winter auch?
         // #3 - Auskühlschutz:  Innentemperatur ist höher als die Mindesttemperatur
         // #4 - Raumfeuchte:    Innenfeuchte ist höher als die Maximalfeuchte
       
         // nicht lüften (oder):
         // #1 - Außenluft ist zu feucht
         // #2 - Außentemperatur zu warm
         // #3 - Innentemperatur niedriger als Mindestraumtemperatur
         // #4 - Innenfeuchte niedriger als Mindestfeuchte
       
         if (debug) log(raum + ":" + cob(b4ln) + " Raumluft ist trocken genug (b4ln): ");
         if (debug) log(raum + ":" + cob(b3ln) + " Außenluft ist zu feucht (b3ln): ");
         if (debug) log(raum + ":" + cob(b2ln) + " Außentemperatur zu warm (b2ln): ");
         if (debug) log(raum + ":" + cob(b1ln) + " Außenluft ist zu feucht (b1ln): " + ": xa: " + xa + " >= (xi - 0.1) " + (xi - 0.1));
         if (debug) log(raum + ": Fenster zu (ein true reicht):");
         
         //if (debug) log(raum + ": b1lp: " + b1lp+ ", b2lp: " + b2lp+ ", b3lp: " + b3lp, b4lp: " + b4lp);
         if (debug) log(raum + ":" + cob(b4lp) + " Raumfeuchte ist niedriger als der Mindstwert (b4lp): "); 
         if (debug) log(raum + ":" + cob(b3lp) + " Innentemperatur ist höher als die Mindesttemperatur (b3lp): ");
         if (debug) log(raum + ":" + cob(b2lp) + " Außentemperatur ist mindestens 0,6 Grad kühler als innen (b2lp): ");
         if (debug) log(raum + ":" + cob(b1lp) + " Außenluft ist mind. 0,4° trockener als Innen (b1lp):  xa: " + xa + " <= (xi - 0.4) " + (xi - 0.4));
         if (debug) log(raum + ": Lüftungsempfehlung (alle Bedingungen auf true):");
       
         if (debug) log(raum + ", ti:"+ti+", ta: "+ta+", xi:"+xi+", xa: "+xa+", mih:"+mih+", mit:"+mit,"info");
         if (debug) log("<b>------ " + raum + " ------- Aussensensor: " + aussen + " -----------</b>");
      }
       
       
       
       
       
      //eric2905 Erzeuge JSON und setzen Variablen "anyLueften" und "countLueften"
      // -----------------------------------------------------------------------------
      function createJSON() {
         // alle Daten im JSON werden als String abgelegt
         if (debug) log("=========================================================");
         if (debug) log("Erzeugung JSON Start");
         if (debug) log("=========================================================");
       
         var anyLueften          = false;
         var countLueften        = 0;
         var raeumeLueftenListe  = [];
         
         var temppfad = "";
         var tempraum = "";
         var tempVal = "";
         var strJSONfinal = "[";
         var strJSONtemp = "";
       
         for (var raum in raeume) {
             strJSONtemp = strJSONtemp + "{";
             strJSONtemp = strJSONtemp + "\"Raum\":\"" + raum + "\",";
       
             for (var datenpunktID in raumDatenpunkte) {
                 
                 // Aussensensor ja oder nein 
                 var aussensensor = false;
                 if (lueftenDp(datenpunktID)) {
                     if (!raeume[raum].Aussensensor) {
                         aussensensor = true;
                     }
                 }
                 
                 temppfad = pfad + raumPfad + raum + "." + raumDatenpunkte[datenpunktID].DpName;
                 tempraum = pfad + raumPfad + raum;
       
                 tempVal = (!aussensensor ? getState(temppfad).val : "");            // kein Aussensenosr: Lüftungsempfehlung auslesen, Aussensensor: Lüftungsempfehlung freilassen
                 if (tempVal === null) tempVal = "";
                 if(raumDatenpunkte[datenpunktID].DpName != "Lüftungsempfehlung") {
                     tempVal = parseFloat(tempVal);
                     tempVal = tempVal.toFixed(2);
                 } else {
                     if (tempVal === true) {
                         anyLueften = true;
                         countLueften = countLueften + 1;
                         raeumeLueftenListe.push(raum);
                     }
                 }
                 strJSONtemp = strJSONtemp + "\"" + raumDatenpunkte[datenpunktID].DpName + "\":\"" + tempVal + "\",";
                 
             }
             strJSONtemp = strJSONtemp.substr(0, strJSONtemp.length - 1);
             strJSONtemp = strJSONtemp + "},";
       
         }
       
         strJSONtemp = strJSONtemp.substr(0, strJSONtemp.length - 1);
         strJSONfinal = strJSONfinal + strJSONtemp + "]";
         if (debug) log("strJSONfinal = " + strJSONfinal);
         if (debug) log("anyLueften = " + anyLueften + ", Anzahl Lüftungsempfehlungen: " + countLueften);
         
         
         setState(pfad + 'Lüften'                    , anyLueften);
         setState(pfad + 'Lüften_Liste'              , writeJson(raeumeLueftenListe));
         setState(pfad + 'Lüften_Anzahl'             , countLueften);
         setState(pfad + 'JSON'                      , strJSONfinal);
         setState(pfad + 'Aktualsierung'             , formatDate(new Date(), strDatum));
         
         if (debug) log("=========================================================");
         if (debug) log("Erzeugung JSON Ende");
         if (debug) log("=========================================================");
      }
      // eric2905 Ende ---------------------------------------------------------------
       
       
       
       
      function calcDelayed(raum, delay) {
         setTimeout(function () {
             calc(raum);
         }, delay || 0);
      }
       
      function creatJSONDelayed() {
         setTimeout(function () {
             createJSON();
         }, 4000); 
      }
       
      // Klimadaten in allen Räumen berechnen 
      function calcAll() {
         for (var raum in raeume) {
             calcDelayed(raum,delayRooms);       // Räume verzögerd nacheinander abarbeiten
         }
      }
       
       
      // finde anhand der Sensor ID einen zugeordneten Raum
      function findRoom(sensor) {
         for (var raum in raeume) {
             if (raeume[raum].Sensor_TEMP == sensor) return raum;
             if (raeume[raum].Sensor_HUM == sensor) return raum;
         }
         return null;
      }
       
      // Änderung eines Sensors (Temperatur oder Luftfeuchtigkeit)
      function valChange(obj) {
         var raumname = findRoom(obj.id);
         if (raumname) {
             if (debug) log('<span style="color:black;"><b>Änderung:' + raumname + ": " + obj.id + ": " + obj.state.val + '</b></span>');
             calcDelayed(raumname,delayRooms);
         }
         // eric2905 Aufruf eingebaut zum JSON erzeugen und Datenpunkt befüllen
         // -----------------------------------------------------------------------------
         creatJSONDelayed();
         // eric2905 Ende ---------------------------------------------------------------
      }
       
       
      // Datenpunkte für alle Räume anlegen
      function createOn() {
         var dpId    = "";
       
         // TODO: Im Modus CONTROL über Objekte: Bei Änderung der OFFSETS, Temperatur_Minimum werden die Änderung erst nach Aktualisierung der Messwerte oder nach Zeit erneuert (auf on() reagieren) 
         var i =0;
       
         for (var raum in raeume) {
       
             if (raeume[raum].Sensor_TEMP) {
                 dpId = raeume[raum].Sensor_TEMP;
                 i++;
                 on({id: dpId ,change:'ne'}, function (obj) {
                     valChange(obj);
                 });
                 if (debug) log("on: " + dpId + " angelegt.");
             }
       
             if (raeume[raum].Sensor_HUM) {
                 dpId = raeume[raum].Sensor_HUM;
                 i++;
                 on({id: dpId ,change:'ne'}, function (obj) {
                     valChange(obj)
                 });
                 if (debug) log("on: " + dpId + " angelegt.");
             }
         }
         log("Subscriptions angelegt: " + i);
      }
       
       
       
      // Schedule
      // =============================================================================
       
      // Nach Zeit alle Räume abfragen
      schedule(cronStr, function () {
         calcAll();
         // eric2905 Aufruf eingebaut zum JSON erzeugen und Datenpunkt befüllen
         creatJSONDelayed();
         // eric2905 Ende ---------------------------------------------------------------
      });
       
       
      // main()
      // =============================================================================
       
      function main() {
         calcAll();
         setTimeout(calcAll,2000);
         // eric2905 Aufruf eingebaut zum JSON erzeugen und Datenpunkt befüllen
         creatJSONDelayed();
         // eric2905 Ende ---------------------------------------------------------------
      }
       
       
      // Skriptstart
      // =============================================================================
       
      createDp();                 // Datenpunkte anlegen
      setTimeout(createOn,2000);  // Subscriptions anlegen
      setTimeout(main,    4000);  // Zum Skriptstart ausführen
      

      Das war Dank @OliverIO kein großes Problem.

      posted in JavaScript
      BananaJoe
      BananaJoe
    • RE: Test Adapter Energiefluss v3.6.x GitHub/Latest

      @skb sagte in Test Adapter Energiefluss v2.0.x GitHub/Latest:

      @skb Ich habe die Idee nun einmal mehr verfolgt und bin inzwischen bei diesem Ergebnis angelangt und würde Euch um Eure Meinung bitten 🙂

      Und noch ein paar Tunnel, Gleise und einen Bahnhof bitte ... 😀

      posted in Tester
      BananaJoe
      BananaJoe

    Latest posts made by BananaJoe

    • RE: Zigbee verliert alle paar Tage alle Fensterkontakte

      @alexhaxe die oben bestellten haben auch AAA Batterien, auch wenn das meine ich vorher nirgends in der Beschreibung stand.
      Die Aquara schaffen aber auch ein Jahr und mehr mit den Knopfzellen

      Ich musste aber sowieso feststellen, das der Batterietyp nichts über die Lebensdauer aussagt.
      Ich hatte für einen kritischen Einsatz extra einen Bodenwassersensor mit 2 AA Batterien bestellt - und der war immer schon nach 2 bis 3 Monaten leer.
      Den betreibe ich zwar immer noch, aber mit so einem USB zu AA Adapter (hatte eine Steckdose in der Nähe)

      posted in Hardware
      BananaJoe
      BananaJoe
    • RE: Quelle für Anzahl der Sonnenstunden?

      @ticaki sagte in Quelle für Anzahl der Sonnenstunden?:

      brightsky

      Der Adapter ist ja cool, kann ja sogar auch noch Solarprognose.
      Er hat auch tatsächlich die Station in Bremen mit ausgewählt die ich auch schon gefunden hatte.

      Dickes Danke!

      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • RE: Quelle für Anzahl der Sonnenstunden?

      Ok, viele Station sind wohl nicht abrufbar wegen nicht mehr da etc., in der letzten Spalte steht ein Datum, alles was länger her ist funktioniert nicht. Alles mit einem aktuellen Datum auch nicht immer.
      Ich habe jetzt eine in Bremen gefunden:
      52555305-0191-4d17-85f7-3cf0a2a2737c-image.png
      https://dwd.api.proxy.bund.dev/v30/stationOverviewExtended?stationIds=10224

      Demnach hatten wir heute 1590 * 0,1 min = 159 Minuten Sonnenschein? 2,5h
      Meine Solaranlage brummte so richtig von 11:30 bis ca. 15:00, also 3,5h,
      Naja, kommt in etwa hin und es sind ja 20km weiter

      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • RE: Quelle für Anzahl der Sonnenstunden?

      Ok, Berlin-Marzan, vermutlich: Da kommt nur eine Zahl an die URL, man kann mehrere angeben.

      https://dwd.api.proxy.bund.dev/v30/stationOverviewExtended?stationIds=G005

      Jetzt muss ich nur eine in der Nähe finden

      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • RE: Quelle für Anzahl der Sonnenstunden?

      @oliverio sagte in Quelle für Anzahl der Sonnenstunden?:

      die dwd api scheint diese informationen enthalten

      6afc0bda-e881-49a7-b3e5-4bff00b0bd14-image.png

      https://dwd.api.bund.dev/
      https://dwd.api.proxy.bund.dev/v30/stationOverviewExtended?stationIds=10865,G005

      dieeinzelwerte sind nicht so gut dokumentiert.
      hier steht ein wenig dazu
      https://github.com/bundesAPI/dwd-api

      Könnte klappen:
      3841aa3f-9dec-4732-ad44-4466f655f635-image.png

      Das weist du woher das G005 kommt bzw. wofür das steht?
      Für eine Station ID, Berlin-Marzahn ..

      Laut dieser Seite wäre Achim bei Bremen 8373:
      https://www.dwd.de/DE/leistungen/klimadatendeutschland/statliste/statlex_html.html?view=nasPublication&nn=16102

      Ich hadere gerade wie denn die URL aussehen muss

      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • Quelle für Anzahl der Sonnenstunden?

      Ich musste am Wochenende meine Integration per daswetter Adapter beenden.
      Ich habe nun auf den OpenWeatherMap-Adapter umgestellt und das klappt sehr gut,
      die Mondphasen haben ich gerade per Skript wieder hinzugefügt ( https://forum.iobroker.net/topic/80342/skript-für-mondphase-mondauf-und-untergang )

      Jetzt würden mir noch 2 1 Werte fehlen die den ich bisher über DasWetter bekommen hatte:

      • Uhrzeit des Sonnenhöchststand
      • Anzahl der Sonnenstunden (die Prognose, nicht einfach errechnet)

      Hätte da jemand eine Idee?

      Den Höchststand habe ich gerade dann doch im FollowTheSun Adapter gefunden:

      followthesun.0.short term.today.solarnoon_time
      
      posted in ioBroker Allgemein
      BananaJoe
      BananaJoe
    • RE: Skript für Mondphase, Mondauf- und Untergang

      @Ro75 und auch danke für diese Skript! Ich musste am Wochenende meine Wetterprognose von "daswetter" beendet und hatte auf OpenWeatherMap umgestellt, da fehlen aber die Mondphasen, die habe ich jetzt wieder.

      posted in JavaScript
      BananaJoe
      BananaJoe
    • RE: Skript für Mondphase, Mondauf- und Untergang

      Ich habe hier noch mal eine neue Version , basierend auf der letzten 1.0.2 Version von @Ro75 erstellt.
      Bei dieser braucht man das extra NPM Modul suncalc nicht hinzufügen, ich habe schlicht die 300 Zeilen davon mit in das Skript kopiert und Anfang + Ende angepasst damit die Funktionen aufrufbar sind.

      So ist das Skript Copy&Paste und läuft "out of the box".
      Ich bin dadurch bisher immer drumherum gekommen irgendwelche NPM Module einbinden zu müssen. Wobei das hier mit 300 Zeilen schon ein längeres ist. Ggf. kann man auch dann noch was nicht genutzt wird wegkürzen.

      //Version 1.0.3 - 12.11.2025
      //Ersteller Ro75.
      //Überarbeitet BananaJoe - Abhängigkeit suncalc entfernt, integriert statt zusätzliches Modul
      
      // Forum: https://forum.iobroker.net/topic/80342/skript-f%C3%BCr-mondphase-mondauf-und-untergang
      // suncalc: https://www.npmjs.com/package/suncalc?activeTab=code
       
      //Voraussetzungen (Version 1.0.1 getestet mit)
      //NodeJS: 20.x / 22.x
      //Javascript-Adapter: 8.9.2
      //Admin-Adapter: 7.7.2
      //JS-Controller: 7.0.7
      
      // #######################################################################################################################################
      // Suncalc direkt hier im Quellcode statt als zusätzliches Modul im JavaScript Adapter
      /*
       (c) 2011-2015, Vladimir Agafonkin
       SunCalc is a JavaScript library for calculating sun/moon position and light phases.
       https://github.com/mourner/suncalc
      */
      
      'use strict';
      
      // shortcuts for easier to read formulas
      
      var PI   = Math.PI,
          sin  = Math.sin,
          cos  = Math.cos,
          tan  = Math.tan,
          asin = Math.asin,
          atan = Math.atan2,
          acos = Math.acos,
          rad  = PI / 180;
      
      // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
      
      
      // date/time constants and conversions
      
      var dayMs = 1000 * 60 * 60 * 24,
          J1970 = 2440588,
          J2000 = 2451545;
      
      function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
      function fromJulian(j)  { return new Date((j + 0.5 - J1970) * dayMs); }
      function toDays(date)   { return toJulian(date) - J2000; }
      
      
      // general calculations for position
      
      var e = rad * 23.4397; // obliquity of the Earth
      
      function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
      function declination(l, b)    { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
      
      function azimuth(H, phi, dec)  { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
      function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
      
      function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
      
      function astroRefraction(h) {
          if (h < 0) // the following formula works for positive altitudes only.
              h = 0; // if h = -0.08901179 a div/0 would occur.
      
          // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
          // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad:
          return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179));
      }
      
      // general sun calculations
      
      function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
      
      function eclipticLongitude(M) {
      
          var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
              P = rad * 102.9372; // perihelion of the Earth
      
          return M + C + P + PI;
      }
      
      function sunCoords(d) {
      
          var M = solarMeanAnomaly(d),
              L = eclipticLongitude(M);
      
          return {
              dec: declination(L, 0),
              ra: rightAscension(L, 0)
          };
      }
      
      
      var SunCalc = {};
      
      
      // calculates sun position for a given date and latitude/longitude
      
      SunCalc.getPosition = function (date, lat, lng) {
      
          var lw  = rad * -lng,
              phi = rad * lat,
              d   = toDays(date),
      
              c  = sunCoords(d),
              H  = siderealTime(d, lw) - c.ra;
      
          return {
              azimuth: azimuth(H, phi, c.dec),
              altitude: altitude(H, phi, c.dec)
          };
      };
      
      
      // sun times configuration (angle, morning name, evening name)
      
      var times = SunCalc.times = [
          [-0.833, 'sunrise',       'sunset'      ],
          [  -0.3, 'sunriseEnd',    'sunsetStart' ],
          [    -6, 'dawn',          'dusk'        ],
          [   -12, 'nauticalDawn',  'nauticalDusk'],
          [   -18, 'nightEnd',      'night'       ],
          [     6, 'goldenHourEnd', 'goldenHour'  ]
      ];
      
      // adds a custom time to the times config
      
      SunCalc.addTime = function (angle, riseName, setName) {
          times.push([angle, riseName, setName]);
      };
      
      
      // calculations for sun times
      
      var J0 = 0.0009;
      
      function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
      
      function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
      function solarTransitJ(ds, M, L)  { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
      
      function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
      function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; }
      
      // returns set time for the given sun altitude
      function getSetJ(h, lw, phi, dec, n, M, L) {
      
          var w = hourAngle(h, phi, dec),
              a = approxTransit(w, lw, n);
          return solarTransitJ(a, M, L);
      }
      
      
      // calculates sun times for a given date, latitude/longitude, and, optionally,
      // the observer height (in meters) relative to the horizon
      
      SunCalc.getTimes = function (date, lat, lng, height) {
      
          height = height || 0;
      
          var lw = rad * -lng,
              phi = rad * lat,
      
              dh = observerAngle(height),
      
              d = toDays(date),
              n = julianCycle(d, lw),
              ds = approxTransit(0, lw, n),
      
              M = solarMeanAnomaly(ds),
              L = eclipticLongitude(M),
              dec = declination(L, 0),
      
              Jnoon = solarTransitJ(ds, M, L),
      
              i, len, time, h0, Jset, Jrise;
      
      
          var result = {
              solarNoon: fromJulian(Jnoon),
              nadir: fromJulian(Jnoon - 0.5)
          };
      
          for (i = 0, len = times.length; i < len; i += 1) {
              time = times[i];
              h0 = (time[0] + dh) * rad;
      
              Jset = getSetJ(h0, lw, phi, dec, n, M, L);
              Jrise = Jnoon - (Jset - Jnoon);
      
              result[time[1]] = fromJulian(Jrise);
              result[time[2]] = fromJulian(Jset);
          }
      
          return result;
      };
      
      
      // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
      
      function moonCoords(d) { // geocentric ecliptic coordinates of the moon
      
          var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
              M = rad * (134.963 + 13.064993 * d), // mean anomaly
              F = rad * (93.272 + 13.229350 * d),  // mean distance
      
              l  = L + rad * 6.289 * sin(M), // longitude
              b  = rad * 5.128 * sin(F),     // latitude
              dt = 385001 - 20905 * cos(M);  // distance to the moon in km
      
          return {
              ra: rightAscension(l, b),
              dec: declination(l, b),
              dist: dt
          };
      }
      
      SunCalc.getMoonPosition = function (date, lat, lng) {
      
          var lw  = rad * -lng,
              phi = rad * lat,
              d   = toDays(date),
      
              c = moonCoords(d),
              H = siderealTime(d, lw) - c.ra,
              h = altitude(H, phi, c.dec),
              // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
              pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H));
      
          h = h + astroRefraction(h); // altitude correction for refraction
      
          return {
              azimuth: azimuth(H, phi, c.dec),
              altitude: h,
              distance: c.dist,
              parallacticAngle: pa
          };
      };
      
      
      // calculations for illumination parameters of the moon,
      // based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
      // Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
      
      SunCalc.getMoonIllumination = function (date) {
      
          var d = toDays(date || new Date()),
              s = sunCoords(d),
              m = moonCoords(d),
      
              sdist = 149598000, // distance from Earth to Sun in km
      
              phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
              inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
              angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
                      cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
      
          return {
              fraction: (1 + cos(inc)) / 2,
              phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
              angle: angle
          };
      };
      
      
      function hoursLater(date, h) {
          return new Date(date.valueOf() + h * dayMs / 24);
      }
      
      // calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
      
      SunCalc.getMoonTimes = function (date, lat, lng, inUTC) {
          var t = new Date(date);
          if (inUTC) t.setUTCHours(0, 0, 0, 0);
          else t.setHours(0, 0, 0, 0);
      
          var hc = 0.133 * rad,
              h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
              h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
      
          // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
          for (var i = 1; i <= 24; i += 2) {
              h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
              h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
      
              a = (h0 + h2) / 2 - h1;
              b = (h2 - h0) / 2;
              xe = -b / (2 * a);
              ye = (a * xe + b) * xe + h1;
              d = b * b - 4 * a * h1;
              roots = 0;
      
              if (d >= 0) {
                  dx = Math.sqrt(d) / (Math.abs(a) * 2);
                  x1 = xe - dx;
                  x2 = xe + dx;
                  if (Math.abs(x1) <= 1) roots++;
                  if (Math.abs(x2) <= 1) roots++;
                  if (x1 < -1) x1 = x2;
              }
      
              if (roots === 1) {
                  if (h0 < 0) rise = i + x1;
                  else set = i + x1;
      
              } else if (roots === 2) {
                  rise = i + (ye < 0 ? x2 : x1);
                  set = i + (ye < 0 ? x1 : x2);
              }
      
              if (rise && set) break;
      
              h0 = h2;
          }
      
          var result = {};
      
          if (rise) result.rise = hoursLater(t, rise);
          if (set) result.set = hoursLater(t, set);
      
          if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
      
          return result;
      };
      
      
      
      
      // #######################################################################################################################################
      
      // const SunCalc = require("suncalc");
      const DPMond = '0_userdata.0.Wetter.';
       
      const LATITUDE = 51.18; // muss an deinen Standort angepasst werden
      const LONGITUDE = 14.43; // muss an deinen Standort angepasst werden
      const SYNODIC_MONTH = 29.530588;
      const FULLMOON_REFERENCE = new Date(2024, 11, 15, 10, 1, 42); // 15. Dezember 2024
       
      const states = [
          { id: 'MondphaseIcon', type: 'number' },
          { id: 'MondphaseProz', type: 'number' },
          { id: 'MondphaseDesc', type: 'string' },
          { id: 'Mondaufgang', type: 'string' },
          { id: 'Monduntergang', type: 'string' }
      ];
       
      states.forEach(({ id, type }) => {
          createState(DPMond + id, type === 'number' ? 0 : '', {
              name: id,
              type,
              read: true,
              write: true
          });
      });
       
      function calculateMoonPhase() {
          const today = new Date();
          const daysSinceReference = (today - FULLMOON_REFERENCE) / 86400000;
          const phaseFraction = (daysSinceReference / SYNODIC_MONTH) % 1;
          const phasePercent = Math.floor(phaseFraction * 100) || 100;
       
          const phaseDescriptions = [
              { range: [0, 25], text: "abnehmender Mond" },
              { range: [25, 25], text: "Halbmond (3. Viertel)" },
              { range: [26, 49], text: "abnehmender Mond" },
              { range: [50, 50], text: "Neumond (4. Viertel)" },
              { range: [51, 74], text: "zunehmender Mond" },
              { range: [75, 75], text: "Halbmond (1. Viertel)" },
              { range: [76, 99], text: "zunehmender Mond" },
              { range: [100, 100], text: "Vollmond (2. Viertel)" }
          ];
       
          const description = phaseDescriptions.find(({ range }) =>
              phasePercent >= range[0] && phasePercent <= range[1]
          )?.text || "unbekannt";
       
          setState(DPMond + 'MondphaseIcon', phasePercent, true);
          setState(DPMond + 'MondphaseDesc', description, true);
      }
       
      function updateMoonTimes() {
          const moonTimes = SunCalc.getMoonTimes(new Date(), LATITUDE, LONGITUDE);
       
          setState(DPMond + 'Mondaufgang', moonTimes.rise ? formatTime(moonTimes.rise) : '--:--', true);
          setState(DPMond + 'Monduntergang', moonTimes.set ? formatTime(moonTimes.set) : '--:--', true);
      }
       
      function formatTime(date) {
          return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false });
      }
       
      function updateMoonIllumination() {
          const illumination = SunCalc.getMoonIllumination(new Date());
          const percent = Math.round(illumination.fraction * 100);
          setState(DPMond + 'MondphaseProz', percent, true);
      }
       
      function updateMoonData() {
          calculateMoonPhase();
          updateMoonTimes();
          updateMoonIllumination();
      }
       
      // Initial run
      updateMoonData();
       
      // Scheduled updates
      schedule('10 * * * *', updateMoonData);
       
      
      
      posted in JavaScript
      BananaJoe
      BananaJoe
    • RE: Homeassistant link in VIS?

      @haselchen Nachtrag: Ich hatte dann doch einen Eintrag gefunden mit Bild (bei meinen Kameras), im hass-Adapter kommt folgender Eintrag:

      hass.0.entities.camera.192_168_1_215.entity_picture
      /api/camera_proxy/camera.192_168_1_215?token=397b896df33a6a706e476263a4c8f59d5f939bc9a366e5fc772b72685fd1c4f
      

      Wenn ich dem Inhalt den Aufruf zum Home Assistant voran setze, bekomme ich das Bild:

      https://ha.meinewebseite.domain/api/camera_proxy/camera.192_168_1_215?token=397b896df33a6a706e476263a4c8f59d5f939bc9a366e5fc772b72685fd1c4f
      

      Ich habe eine Reverse Proxy davor, bei euch wäre es dann so etwas:

      http://192.168.0.8:8123/api/camera_proxy/camera.192_168_1_215?token=397b896df33a6a706e476263a4c8f59d5f939bc9a366e5fc772b72685fd1c4f
      
      posted in Visualisierung
      BananaJoe
      BananaJoe
    • RE: Homeassistant link in VIS?

      @haselchen ich habe gerade mal bei mir geschaut - was steht denn da bei dir drin?
      Gerne auch den ganzen Pfad im hass-Adapter dazu.
      Im Screenshot im ersten Post ist ja kein richtiges Bild hinterlegt (da fehlt die Dateiendung).

      Ich hatte gerade mal bei mir geschaut, Bilder habe ich ich aber nur bei meine Life360 Integration - und da ist eine richtige URL abgelegt.

      posted in Visualisierung
      BananaJoe
      BananaJoe
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo