nocin.eu

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

[Home Assistant] Midea Comfee MSAF5-12HRDN8-QE R32

Vor kurzem haben wir uns eine Klimaanlage zugelegt. Diese nutzen wir zum Heizen und Kühlen unseres Büros. Dabei handelt es sich um dieses Gerät der Marke Midea (in DE vor allem als Comfee vermarktet).

Der schlimmste Teil der Installation war wie so häufig nicht die Montage, sondern die Einrichtung des Innengerätes über die App. Um das Gerät in das lokale Wifi zu bringen, muss man sich mit einem Access Point des Gerätes verbinden und dann in der App eine Einrichtung durchlaufen. Nach mehreren gescheiterten Versuchen mit einem Android Telefon, habe ich dann zu einem iPhone gegriffen. Hiermit klappte die Einrichtung erheblich besser! Obwohl das Gerät dann im lokalen Netz erreichbar ist, erfolgt die Steuerung dann über eine Cloud… mit entsprechend langsamen Reaktionszeiten. Meiner Meinung nach quasi unbenutzbar.

Um das Gerät ohne großes manuelles Zutun betreiben zu können (also ohne App oder Fernbindung), habe ich daher nach einer entsprechenden Home Assistant Integration gesucht.


Also erstes stieß ich auf diese: https://github.com/nbogojevic/homeassistant-midea-air-appliances-lan

So ganz habe ich dabei nicht verstanden, ob die Integration nun lokal oder via der Cloud steuert. Zumindest musste man beim Einrichtung seine Cloud Zugangsdaten angeben und darüber wird dann das lokale Gerät ermittelt. Beim Testen fiel mir auf, dass sobald man diese Integration nutzte, das Gerät nicht mehr über die App steuerbar war (und umgekehrt). Der Grund ist, dass man den gleichen Zugang nicht auf zwei verschiedenen Geräten nutzen kann. Nach einer Einrichtung eines zweiten Accounts trat das Problem nicht mehr auf und man konnte beides parallel nutzen.

So richtig zuverlässig funktionierte das Ein- oder Ausschalten des Gerätes bei mir jedoch nicht. Ggf. weil die Befehle auch über die Cloud liefen und nicht lokal!?

Auch die Entities der Integration schienen für die Klimaanlage nicht vollständig zu sein. Insgesamt schien die Integration eher für anderen Midea Gerätetypen geeignet zu sein, als Klimaanlagen.


Darauf hin habe ich mir diese Integration angeschaut und bin aktuell auch bei dieser geblieben: https://github.com/georgezhao2010/midea_ac_lan

Die Integration bietet die Möglichkeit, selber die entsprechen Sensoren und Controls zu aktivieren. Man muss also durch Trial-and-Error herausfinden, welche von dem eigenen Gerät unterstützt bzw. publiziert werden

oder in den Developer Tools nach der zugehörigen Climate Entity suchen und sich die Werte anschauen.

Nach etwas herumprobieren, bin ich bei dieser Auswahl geblieben.

Leider funktioniert die Realtime Power nicht, aber immerhin der Gesamtverbrauch. Ich habe daher noch eine Zigbee Steckdose mit Strommessung davor geschaltet, um den Verbrauch in Echtzeit zu überwachen. Außerdem konnte ich die Indoor und Outdoor Temperature Werte noch nicht wirklich nachvollziehen. Sie verändern sich m.M.n. recht merkwürdig. In der vorherigen Integration wurden hier irgendwie genauere Werte angezeigt. Jemand hat dazu auch bereits ein Issue aufgemacht.


Eine entsprechende Thermostat Card wird auch direkt auf dem Dashboard erzeugt.

Eine etwas schönere Anzeige bekommt man mit der Simple Thermostat Card:

type: custom:simple-thermostat
entity: climate.146235046568457_climate
step_size: 1
sensors:
  - entity: sensor.lumi_lumi_weather_temperature
    name: Büro
header: {}
layout:
  mode:
    icons: true
    names: true


Der wirklich interessante Teil ist dann natürlich erst durch Automationen zu verwirklichen. Ich lasse die Klimaanlage z.B. automatisiert bei entsprechenden Uhrzeiten und Temperaturen einschalten. Oder auch, wenn z.B. grade ausreichend Strom von dem Balkonkraftwerk erzeugt wird. Und natürlich wird sie automatisch ausgeschaltet, wenn alle das Haus verlassen.

Insgesamt funktioniert die Integration nun seit einigen Wochen sehr zuverlässig.


Update 08.07.2024: In diesen Reddit Thread wird mehrfach erwähnt, dass man ggf. auch den Wi-Fi-Stick mit einer ESP basierten Lösung ersetzten kann, wie z.B. dieser hier. Das werde ich aber erst testen, wenn es mit der aktuellen Lösung zu Problemen kommt.

Update 10.01.2025: Mit dem Update von HA auf Version 2025.1.2 funktioniert die Integration https://github.com/georgezhao2010/midea_ac_lan nicht mehr. Anscheinend pflegt der Maintainer dieser nicht mehr. Zum Glück führt jemand die Entwicklung in diesem Fork weiter: https://github.com/wuwentao/midea_ac_lan. Damit auch alle Sensoren wieder angezeigt werden, musste ich nach Installation und HA Restart noch in der Integration bei dem Midea Gerät ein “Reload” durchführen.

YouTube ad-free consumption and other helpful addons

When I was at a friend’s house the other day and a YouTube playlist was playing on the TV, I was really shocked. After every video, there were one or two ads (and some of them even in between). I was not aware how extremely many ads you get when using YouTube without an ad blocker. Therefore, a small collection of apps, add-ons and links that help to make YouTube a bit more enjoyable:

Browser

AndroidTV / FireTV

Android

HTPC

DNS Blocking (i.e. with PiHole)

Update 15.01.2024: When using YouTube in a Browser in combination with uBlock, you likely receive the following message right now: “Ad blockers are not allowed on YouTube”. The only working solutions to prevent this message and to continue watching ad -free is disabling uBlock on YouTube and use this Script with Tampermonkey (at least for me).

Update 25.01.2025: I found Chrome Mask very helpful, as I continue to experience problems when using uBlock in combination with Firefox on YouTube.

[Home Assistant] Presence detection

There are different ways to realize presence detection in Home Assistant:

https://www.home-assistant.io/getting-started/presence-detection/

As I have a FritzBox at home, I’m using the AVM FRITZ!Box Tools Integration, which has “presence detection by looking at connected devices”.

You can find your devices using the developer tools and looking for the device_tracker entity. I then use the devices in a group to easily check if anyone is home.

/config/groups.yaml

family:
  - device_tracker.xiaomiredminote8pro
  - device_tracker.xioamimi8

Update 19.04.2024: Instead of a group, you could also use the zone.home entity and check if its numeric value is 0 (not_home) or above 0 (home) in your automations. The zone.home entity relates to the person and the device that belong to a person, that you can configure under Settings → People → Select a Person → Select the devices that belong to this person.

[Android] Upgrading LineageOS 17.1 to 18.1 on my Xiaomi Mi 8 (dipper)

Install the Android Debug Bridge (ADB)

https://wiki.lineageos.org/adb_fastboot_guide.html
https://github.com/M0Rf30/android-udev-rules#installation

# check if device is found
adb devices
# reboot into sideload modus
adb reboot sideload

Or manually boot into TWRP recovery, holding Volume Up + Power when the phone is off. Navigate to Advanced-> ADB Sideload.


Update MIUI Firmware

Following the docs, I first had to check the Firmware version. I was running V12.0.2.0.QEAMIXM, but V12.0.3.0.QEAMIXM is required.
Download the right MIUI Firmware for your device from https://xiaomifirmwareupdater.com/firmware/dipper/.
Flash the new Firmware via TWRP or via ADB sideload.

adb sideload Downloads/fw_dipper_miui_MI8Global_V12.0.3.0.QEAMIXM_7619340f8c_10.0.zip 

Download and flash new LineageOS image

I’m using the LineageOS fork LineageOS for microG. Download it from here: https://download.lineage.microg.org/dipper/ (MI 8 = dipper)
The upgrade steps are the same as for the official rom: https://wiki.lineageos.org/devices/dipper/upgrade. In my case only flashing the new image.

adb sideload Downloads/lineage-18.1-20220602-microG-dipper.zip 

[ABAP] ALV traffic lights

TYPES: BEGIN OF ty_log,
         status TYPE icon-id,
       END OF ty_log.

DATA gt_log TYPE TABLE OF ty_log.

gt_log = VALUE #( ( status = icon_red_light    )
                  ( status = icon_yellow_light )
                  ( status = icon_green_light  ) ).

TRY.
    cl_salv_table=>factory( IMPORTING r_salv_table = DATA(alv_table)
                            CHANGING  t_table      = gt_log ).

    alv_table->display( ).

  CATCH cx_salv_msg.
ENDTRY.

icon_red, icon_yellow etc. will be automatically loaded from the TYPE-POOL: icon during runtime.

You can check all available icons via the table icon.

SELECT SINGLE id INTO @DATA(icon_red) FROM icon WHERE name = 'ICON_RED_LIGHT'.

[PDF.js] Set filename when downloading pdf

When using a blob or base64 data to open a file in PDF.js, the viewer is not able to get the correct filename. So when downloading the pdf using the download button inside the viewer, it will always download as document.pdf. To manually set a filename, you can use the setTitleUsingUrl function.

<iframe id="pdf-js-viewer" src="/pdf/web/viewer.html?file=" title="webviewer" frameborder="0" width="100%" height="700" allowfullscreen="" webkitallowfullscreen=""/>
let pdfViewerIFrame = document.getElementById("pdf-js-viewer")
pdfViewerIFrame.contentWindow.PDFViewerApplication.setTitleUsingUrl("myFilename")

More info here: https://github.com/mozilla/pdf.js/issues/10435

[ABAP] Download Transport as ZIP

Related Reports:
https://nocin.eu/abap-import-transport-from-zip/
https://nocin.eu/abap-create-toc-for-a-given-transport-release-it-and-download-it-as-zip/

This report can be handy, especially since S/4HANA 2023 seems to have restricted the “classic” import way by using TCode CG3Y and CG3Z. See note 1949906, where it is recommended to create a custom report.

*&---------------------------------------------------------------------*
*& Report ZIP_TRANSPORT
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zip_transport.

SELECTION-SCREEN BEGIN OF BLOCK bl01 WITH FRAME TITLE TEXT-t01.
PARAMETERS p_trkorr LIKE e070-trkorr OBLIGATORY.
PARAMETERS p_ttext  TYPE as4text.
SELECTION-SCREEN END OF BLOCK bl01.

SELECTION-SCREEN BEGIN OF BLOCK bl02 WITH FRAME TITLE TEXT-t02.
PARAMETERS p_sapdir TYPE string LOWER CASE OBLIGATORY DEFAULT '/usr/sap/trans/'.
PARAMETERS p_lcldir TYPE string LOWER CASE OBLIGATORY DEFAULT 'C:\temp\'.
SELECTION-SCREEN END OF BLOCK bl02.


START-OF-SELECTION.

  " Check if Transport is released
  DATA ls_request TYPE trwbo_request.
  CALL FUNCTION 'TR_READ_REQUEST'
    EXPORTING
      iv_read_e070     = abap_true
      iv_read_e07t     = abap_true
      iv_trkorr        = p_trkorr
    CHANGING
      cs_request       = ls_request
    EXCEPTIONS
      error_occured    = 1
      no_authorization = 2
      OTHERS           = 3.
  IF ls_request-h-trstatus <> 'R'.
    MESSAGE 'Transport not yet released' TYPE 'E'.
  ENDIF.


  " Read released Transport
  DATA lv_xcontent_k TYPE xstring.
  DATA lv_xcontent_r TYPE xstring.

  DATA(lv_transdir_k) = |{ p_sapdir }cofiles/K{ p_trkorr+4 }.{ p_trkorr(3) }|.
  DATA(lv_transdir_r) = |{ p_sapdir }data/R{    p_trkorr+4 }.{ p_trkorr(3) }|.


  TRY.

      " K
      OPEN  DATASET lv_transdir_k FOR INPUT IN BINARY MODE.
      READ  DATASET lv_transdir_k INTO lv_xcontent_k.
      CLOSE DATASET lv_transdir_k.

      " R
      OPEN  DATASET lv_transdir_r FOR INPUT IN BINARY MODE.
      READ  DATASET lv_transdir_r INTO lv_xcontent_r.
      CLOSE DATASET lv_transdir_r.

      " Add to ZIP
      DATA(lo_zipper) = NEW cl_abap_zip( ).

      lo_zipper->add( name    = |K{ p_trkorr+4 }.{ p_trkorr(3) }|
                      content = lv_xcontent_k ).

      lo_zipper->add( name    = |R{ p_trkorr+4 }.{ p_trkorr(3) }|
                      content = lv_xcontent_r ).


      " Download ZIP
      DATA(lv_xzip) = lo_zipper->save( ).

      " Convert to raw data
      DATA(lt_data) = cl_bcs_convert=>xstring_to_solix( iv_xstring = lv_xzip ).

      " Set zip filename
      DATA(lv_zip_name) = COND #( WHEN p_ttext IS INITIAL THEN |{ ls_request-h-as4text }_{ p_trkorr }|
                                                          ELSE |{ p_ttext              }_{ p_trkorr }| ).

      " Replace every character that is not [a-zA-Z0-9_] with '_'.
      REPLACE ALL OCCURRENCES OF REGEX '[^\w]+' IN lv_zip_name WITH '_'.

      cl_gui_frontend_services=>gui_download( EXPORTING filename = p_lcldir && lv_zip_name && '.zip'
                                                        filetype = 'BIN'
                                              CHANGING  data_tab = lt_data ).

    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'E'.
  ENDTRY.

  MESSAGE |{ lv_zip_name }.zip created and downloaded to { p_lcldir }| TYPE 'S'.

[CAP] Download MTA deployment logs after deployment

With this simple command, you can download the MTA deployment logs of your recent deployment. Instead of manually having to use cf dmol -i <operation id>, just save the command (replace appId with your Id) as a shell script, make it executable (chmod +x) and run the script after your deployment. It stores the recent logs into the folder logs.

dmol is short for download-mta-op-logs

cf download-mta-op-logs --mta <appId> --last 1 -d logs  

To just have a quick look at the recent logs, use

cf logs <appId>-srv --recent

[CAP] Check if User exists in SuccessFactors

To check if a record exists, you can simply use the HEAD query.

“HEAD is almost identical to GET, but without the response body.” (https://www.w3schools.com/tags/ref_httpmethods.asp)

Unfortunately, there is no shortcut in CAP for a HEAD query, so simply use send with method HEAD.

    sfsfSrv = await cds.connect.to('sfsf')

    try {
        await sfsfSrv.send('HEAD', `/User('${userId}')`)
        return true  // status 200
    } catch (err) {
        return false // status 404
    }