Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[SAPUI5] Get data of an Item of a List or Table

All options have in common that you first try to get the binding context from the list/table element via the event. Having the right context, you can either use the getProperty() function to get a specific property, or use the getObject() function to get all data.

onClick: function (oEvent) {
    // Option 1
    oEvent.getParameters().item.getBindingContext().getProperty("ID") // when having a list 
    // Option 2
    oEvent.getParameters().item.getBindingContext().getObject().ID
    // Option 3
    oEvent.getParameter("item").getBindingContext().getObject().ID 
    // Option 4
    oEvent.getSource().getBindingContext().getObject().ID 
}

Note: When using a List, it’s oEvent.getParameters().listItem instead of oEvent.getParameters().item.

Or you could also use the sPath property from the binding context and directly get the data from the model.

onClick: function (oEvent) {
    // Option 5
    const sPath = oEvent.getSource().getBindingContext().sPath 
    // 5a
    this.getView().getModel().getProperty(sPath).ID 
    // 5b
    this.getView().getModel().getProperty(sPath + "/ID") 
}

[Linux Mint] Wemos D1 Mini (ESP8266) not recognized

I had a few D1 Minis lying around that I wanted to flash WLED onto. But when plugging into my main PC running Linux Mint 21, no device got recognized. The D1 Mini was just flashing its blue LED light 2 times and that was all. I had already checked before, whether the USB cable is also a Data Link cable, because now and then you accidentally grab a USB cable which is charging only.

Since I was pretty sure it wasn’t a hardware problem, I checked dmesg for any suspicious messages. I’m using an alias named klog to beautify the output.

sudo dmesg -t -L=never -l emerg,alert,crit,err,warn --human --nopager

And indeed there were some messages regarding USB.

usbfs: interface 0 claimed by ch341 while 'brltty' sets config #1

Since brltty is software for people with visual impairment, I don’t need it and therefore uninstalled it.

sudo apt remove brltty
sudo apt autoclean && sudo apt autoremove

And after removing brltty, my D1 Mini got recognized immediately. Fortunately, the solution was very simple. 🙂

Update 19.01.2024: If you receive Cannot open /dev/ttyUSB0: Permission denied errors, when writing to the esp, you have to add your user to the dialout group and re-login. (*)

sudo usermod -a -G dialout $USER

[Home Assistant] Notify Groups

Notify Groups are useful to send notifications to a group of devices or even to different channels like Home Assistant Companion App, Telegram, Signal etc.

https://www.home-assistant.io/integrations/group/#notify-groups

I’m using it to create a group of Android Devices I want to send notifications to via the Companion App.

# Notify Groups
notify:
  - platform: group
    name: "family"
    services:
      - service: mobile_app_mi_8
      - service: mobile_app_redmi_note_8_pro

After that, go to Developer Tools and reload the Notify Services.

Now you should find a new notify service called “family” which can be used in automations.

[PDF.js] Set light and dark theme manually

The new PDF.js viewer design Photon supports a light and a dark mode. By default, the theme is set automatically. To overwrite this, you can set the viewerCssTheme property.

I’ve embedded my PDF viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

By setting the viewerCssTheme property, you are able to change the PDF viewer theme. It can be modified using the PDFViewerApplicationOptions.set() function and there are three possible values. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here). In my case, I also had to call _forceCssTheme() after that.

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    // https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("viewerCssTheme", 1)       // 0=automatic theme, 1=light theme, 2=dark theme 
                    pdfViewerIFrame.contentWindow.PDFViewerApplication._forceCssTheme()
                })

Stromzähler auslesen: LOGAREX LK13BE803049

Wurde der Stromzähler in den letzten Jahren getauscht, solltet ihr eine moderne Messeinrichtung haben, welche eine Infrarot (IR) Schnittstelle für das Auslesen von Daten anbietet. Dazu gibt es bereits eine große Anzahl an Anleitungen, z. B. hier und hier. Daher nur kurz zusammengefasst, welche Schritte notwendig waren für meinen LOGAREX LK13BE803049 (Baujahr 2018).

Stromzähler vorbereiten

Um Zugriff auf alle Daten, die der Stromzähler so sammelt zu bekommen, benötigt man eine PIN. Diese PIN kann man über das Online-Portal des Messstellenbetreibers herausfinden, alternativ geht das auch via Hotline oder Mail. Hat man seine PIN, am besten in der Anleitung nachschlagen, wie die PIN eingeben (Seite 6) und wie der “vollständiger Datensatz” (Seite 8) aktiviert wird. Ansonsten bekommt man nur einen Bruchteil der vorhanden Daten zurück.

IR Lese-Schreibkopf

Kann man selber basteln für wenige Euro oder alternativ fertig kaufen. Ich habe diesen (WIFI lese-schreib-Kopf EHZ Volkszähler Hichi Smartmeter) gekauft, da hier ein ESP32 dabei ist und dieser sogar bereits mit Tasmota geflasht wurde. Um Abstürze im Betrieb zu verhindern, darauf achten, dass das Netzteil der Micro-USB Stromversorgung genug Leistung hat. Ist die Stromversorgung sichergestellt, mittels Smartphone mit dem vom ESP32 geöffneten Access Point verbinden und die WLAN Daten des Heimnetzes im Tasmota Webinterface eingeben (sollte sich automatisch öffnen nach der AP Verbindung). Nun kann das ganze Teil mithilfe des Magneten am Stromzähler über der IR Schnittstelle (Optische Datenschnittstelle D0) angebracht werden. Im Idealfall hat man dafür noch eine freie Steckdose in der Nähe des Stromzählers.

Script anlegen in Tasmota

Erneut das Tasmota Webinterface aufrufen, am besten diesmal vom Desktop PC, und über den Button Consoles zu Edit Script navigieren. Dort müssen nun einige Zeilen eingetragen werden, die sich je nach Stromzähler unterscheiden können. Eine Übersicht der möglichen Werte des LOGAREX Zählers findet ihr in der Anleitung auf Seite 10. Bei mir sah das Ergebnis dann so aus:

>D
>B
=>sensor53 r
>M 1
+1,3,o,0,9600,LOGAREX
1,1-0:1.8.0*255(@1,BEZUG,KWh,total_in,4
1,1-0:2.8.0*255(@1,EINSPEISUNG,KWh,total_out,4
1,1-0:16.7.0*255(@1,Verbrauch aktuell,W,current,16
1,1-0:1.8.0*96(@1,letzter Tag,KWh,total_day,1
1,1-0:1.8.0*97(@1,letzte Woche,KWh,total_week,1
1,1-0:1.8.0*98(@1,letzter Monat,KWh,total_month,1
1,1-0:1.8.0*99(@1,letztes Jahr,KWh,total_year,1
#

Home Assistant

Via MQTT und Tasmota Integration können die Werte direkt an Home Asisstant übertragen werden und z.B. im Energy Dashboard verwendet werden. Dafür einfach die Schritte unter 4b von dieser Anleitung umsetzten. Falls MQTT auf eurem Home Assistant noch nicht eingerichtet ist, müsst ihr das noch tun. Dafür gibt es auch einen Haufen Anleitungen oder Videos. Am besten einfach eine recht aktuelle Anleitung heraussuchen. Bei der Einrichtung legt ihr auch einen separaten User für MQTT an, welchen ihr dann angeben müsst in Tasmota. So sah es bei mir dann aus.

In der Tasmota Integration in Home Assistant wurde das Device mit entsprechen Entitäten direkt gefunden.

Da die Werte jedoch ohne Einheit übertragen werden, muss diese noch händisch in der configuration.yaml gepflegt werden. Wenn ich mich recht erinnere, ist danach ein Neustart von Home Assistent erforderlich.

 # Tasmota Stromzähler
homeassistant:
  customize:
    sensor.tasmota_logarex_total_in:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing
    sensor.tasmota_logarex_total_out:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing
    sensor.tasmota_logarex_current: 
      device_class: power 
      unit_of_measurement: "W"
    sensor.tasmota_logarex_total_day:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing
    sensor.tasmota_logarex_total_week:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing  
    sensor.tasmota_logarex_total_month:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing  
    sensor.tasmota_logarex_total_year:
      device_class: energy 
      unit_of_measurement: "kWh"
      state_class: total_increasing  

So sieht die Anzeige der Sensoren direkt etwas brauchbarer aus. 🙂

Die schöne Energieverteilungsanzeige vom Energy Dashboard kann auch in jedem anderen Dashboard verwendet werden via:

type: energy-distribution
link_dashboard: true

Einen Überblick der weiteren Energy Cards findet man hier.

Im Energy Dashboard kann auch ein Preis für die kWh hinterlegt werden. So sieht man direkt seinen Verbrauch für ausgewählte Zeiträume. Hier mal ein extremes Beispiel, an dem wir den ganzen Tag Äpfel eingekocht und Kuchen gebacken haben. So ein Verbrauch ist bei uns natürlich nicht die Regel. 🙂

Für mein Smartphone Dashboard habe ich dann noch die neue Statictics Card genutzt, um einen noch einfacheren Einblick in meine laufenden Kosten zu bekommen. Die Anordnung erfolgt durch eine Grid Card.

square: false
columns: 2
type: grid
cards:
  - type: statistic
    entity: sensor.tasmota_logarex_total_in_cost
    period:
      calendar:
        period: day
    stat_type: change
    name: Kosten heute
  - type: statistic
    entity: sensor.tasmota_logarex_total_in_cost
    period:
      calendar:
        period: day
        offset: -1
    stat_type: change
    name: Kosten gestern

[PDF.js] Allow PDF documents to disable copying in the viewer

With pull request 11789, the PDF Viewer respects the PDF property Content Copying, if the viewer option enablePermissions is set to true.

I’ve embedded my viewer in an iFrame like this:

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>

The enablePermissions property can be modified using the PDFViewerApplicationOptions.set() function. The property has to be set before the Viewer is initialized. To archive this, you can listen to the event webviewerloaded (read more about it here).

                document.addEventListener("webviewerloaded", async () => {
                    let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
                    //https://github.com/mozilla/pdf.js/blob/master/web/app_options.js
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("enablePermissions", true) //allow PDF documents to disable copying in the viewer
                    pdfViewerIFrame.contentWindow.PDFViewerApplicationOptions.set("defaultUrl", "") //prevent loading default pdf
                    }
                })

[JavaScript] Find specific object from an array of nested objects

I was again working on a task for my hierarchical tree structure (see previous post here). This time I needed to find a specific object with a specific property value.

So here again my nested array:

const aTreeData = [{
    "NodeId": 1,
    "HierarchyLevel": 1,
    "type": "folder",
    "nodes": [{
        "NodeId": 2,
        "HierarchyLevel": 2,
        "type": "folder",
        "nodes": [{
          "NodeId": 3,
          "HierarchyLevel": 3,
          "type": "category"
        }]
      },
      {
        "NodeId": 4,
        "HierarchyLevel": 2,
        "type": "category",
        "nodes": [{
          "NodeId": 5,
          "HierarchyLevel": 3,
          "type": "file"
        }]
      }
    ]
  },
  {
    "NodeId": 6,
    "HierarchyLevel": 1,
    "type": "folder",
    "nodes": [{
      "NodeId": 7,
      "HierarchyLevel": 2,
      "type": "category"
    }]
  }
]

I needed a function that was able to find any object by providing only the NodeId value. Fortunately I stumbled accross some excellent code snippet by Scott Sauyet which is exactly doing what I needed:

https://stackoverflow.com/questions/68559392/find-object-from-an-array-of-nested-objects-by-key-in-javascript

I just had to change the properties to match my objects and it was working!

const findNodeInTreeData = (aTreeData, nodeId) => {
  const deepFind = pred => ([x, ...xs] = []) =>
    x && (pred(x) ? x : deepFind(pred)(x.nodes) || deepFind(pred)(xs))

  const findByNodeId = id => obj =>
    deepFind(o => o.NodeId == id)([obj])

  return findByNodeId(nodeId)(aTreeData[0])
}

console.log(findNodeInTreeData(aTreeData, 5))