Per RESTful Command Logfile einer INSTAR Kamera auslesen und darstellen

Moin

Ich habe im Außenbereich eine Kamera von Instar, die sich über Homeassi per CGI Befehle (RESTful Command - Home Assistant)) steuern läßt, siehe auch https://wiki.instar.com/de/1440p_Serie_CGI_Befehle/ Mit diesen habe ich die letzten Stunden experimentiert.

Das Auslesen des Kamera Logfiles war eine Herausforderung und ich möchte die Lösung kurz für Nachbauer dokumentieren. Falls einer weiß, wie es einfacher geht, bitte gerne schreiben.

Was ich für den Logfile verwende

rest_command:
  get_system_log:
    url: "http://192.xxx.xxx.xxx/param.cgi?cmd=getsyslog&user=xxx&pwd=xxx"
    method: GET
  • einen MQTT Sensor ebenfalls in der configuration.yaml
mqtt:
  sensor:
    - name: carport_camera_logfile
      unique_id: carport_camera_logfile
      state_topic: "camera/log/trigger"
      value_template: "valid"
      json_attributes_topic: "camera/log/data"

In diesem Sensor wird das Logfile der Kamera als Atrribut zwischengespeichert. Attribut deshalb weil man ansonsten der 255 Zeichen Beschränkung unterliegt.

Ich hatte zuerst viel mit Restful Sensor probiert aber es nicht hinbekommen.

Ihr solltet für diesen Sensor in Eurem Recorder die History deaktivieren weil ansonsten sich schnell die DB Größe erhöht.

  • ein Script, das den rest_command triggert und dessen Antwort verarbeitet
alias: om_scr_draussen_carport_kamera_refresh_logfile
description: >-
  ### Holt sich per rest_command den logfile der Kamera und veröffentlicht über
  mqtt
sequence:
  - action: rest_command.get_system_log
    response_variable: logfile
    data: {}
  - delay:
      seconds: 2
  - if:
      - condition: template
        value_template: "{{ logfile['status'] == 200 }}"
    then:
      - action: mqtt.publish
        data:
          topic: camera/log/data
          payload: |2-
              {
                "log_content": {{ (
                  logfile['content']
                  | regex_findall('(?s)log="(.*?)";')
                  | first
                  | default('')
                ) | tojson }}
              }
          retain: true
    else:
      - action: mqtt.publish
        data:
          topic: camera/log/data
          payload: |2-
              {
                "log_content": "Kamera Logfile konnte nicht gelesen werden."
              }
          retain: true
icon: mdi:webcam

Das Script stoße ich über den Refresh Button auf dem Dashboard an. Theorethisch könnte man dies auch automatisieren.

Das Logfile kommt nicht als Zeilen separiert an, sondern in Variablen log=“…” und dieser Inhalt wird gleich extrahiert. Eigentlich wollte ich das später mit Jinja machen aber dann kam es hier immer zu einem Fehler, “out of index” und der Logfile war nicht komplett.

  • einen input_number Helfer, mit dem ich die Anzahl der angezeigten Logzeilen varieren kann.
input_number.camera_logfile_lines

Man könnte diesen Helfer direkt auf dem Dashboard veränderbar machen, aber ich habe mich für +/- Buttons entschieden, die jeweils die Anzahl der Zeilen um 1 erhöhen/reduzieren.

  • und eine Markdownkarte, in dem die gewünschte Logfiles angezeigt werden mit etwas Datum/Uhrzeit Kosmetik um Platz zu sparen
type: markdown
content: |-
  {% set number_of_lines = states('input_number.camera_logfile_lines') | int -%}
  {% set raw = state_attr('sensor.carport_camera_logfile', 'log_content') -%}
  {% if raw != none -%}
    {% set lines = raw.split('\n') | map('trim') | reject('equalto', '') | list%}
    {# Empfangene Liste is aufsteigend aber ich will absteigend #}
    {% set last = lines[-number_of_lines:] | reverse -%}
    #### Zeige letzten {{number_of_lines}} Logfile Einträge
    {% for line in last -%}
      {% set parts = line.split(':', 3) -%}
      {% if parts | length >= 3 -%}
        {% set ts = parts[0] ~ ':' ~ parts[1] ~ ':' ~ parts[2] -%}
        {% set dt = ts | as_datetime(default=None) -%}
        {% if dt -%}
          {{ dt.strftime('%d.%m. %H:%M') }} {{ parts[3] | default('') }}
        {% else -%}
          {{ line }}
        {% endif -%}
      {% else -%}
        {{ line }}
      {% endif -%}
    {% endfor -%}
  {% else -%}
    Kein Logfile geladen
  {% endif -%}

Im Dashboard Abschnitt

habe ich viel mit custom:button-card gearbeitet. Falls jemand das nachbauen möchte, hier der Code

Zusammenfassung
type: grid
cards:
  - type: heading
    icon: mdi:webcam
    heading: Carport
    heading_style: title
  - type: entities
    entities:
      - entity: switch.carportkamerashelly_switch_0
        name: Ein/Aus schalten
      - entity: sensor.carportkamerashelly_switch_0_power
    state_color: true
    show_header_toggle: false
    card_mod:
      style: |
        ha-card {
          box-shadow: 0px 2px 4px 0px rgba(128,128,128,0.66);
        }
  - type: custom:webrtc-camera
    entity: camera.kameracarport
    title: Carport 39
    mouse_drag_pan: true
    mouse_wheel_zoom: true
    mouse_double_click_zoom: true
    touch_drag_pan: true
    touch_pinch_zoom: true
    touch_tap_drag_zoom: true
    persist: true
    muted: true
    ui: true
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: vertical-stack
    cards:
      - type: horizontal-stack
        cards:
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-1-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_1
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-2-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_2
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-3-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_3
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-4-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_4
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-5-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_5
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-6-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_6
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-7-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_7
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:numeric-8-box-outline
            size: 5
            tap_action:
              action: perform-action
              service: rest_command.goto_position_8
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: vertical-stack
    cards:
      - type: horizontal-stack
        cards:
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:bell-outline
            size: 30%
            tap_action:
              action: perform-action
              service: rest_command.enable_alert
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:bell-off-outline
            size: 30%
            tap_action:
              action: perform-action
              service: rest_command.disable_alert
              confirmation:
                text: Soll der Alarm wirklich dauerhaft deaktiviert werden ?
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:volume-high
            size: 30%
            tap_action:
              action: perform-action
              service: rest_command.enable_audio
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:volume-mute
            size: 30%
            tap_action:
              action: perform-action
              service: rest_command.disable_audio
              confirmation:
                text: Soll Audio wirklich deaktiviert werden ?
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: custom:button-card
    icon: mdi:record-rec
    name: Starte kurze Alarm Aufnahme
    size: 40%
    layout: icon_name
    tap_action:
      action: perform-action
      service: rest_command.trigger_alert_recording
    styles:
      card:
        - padding: 5px
      grid:
        - grid-template-areas: "\"i n\""
        - grid-template-columns: auto 1fr
      icon:
        - width: 50px
        - height: 50px
      name:
        - justify-self: start
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: markdown
    content: >-
      {% set number_of_lines = states('input_number.camera_logfile_lines') | int
      -%}

      {% set raw = state_attr('sensor.carport_camera_logfile', 'log_content')
      -%}

      {% if raw != none -%}
        {% set lines = raw.split('\n') | map('trim') | reject('equalto', '') | list%}
        {# Empfangene Liste is aufsteigend aber ich will absteigend #}
        {% set last = lines[-number_of_lines:] | reverse -%}
        #### Zeige letzten {{number_of_lines}} Logfile Einträge
        {% for line in last -%}
          {% set parts = line.split(':', 3) -%}
          {% if parts | length >= 3 -%}
            {% set ts = parts[0] ~ ':' ~ parts[1] ~ ':' ~ parts[2] -%}
            {% set dt = ts | as_datetime(default=None) -%}
            {% if dt -%}
              {{ dt.strftime('%d.%m. %H:%M') }} {{ parts[3] | default('') }}
            {% else -%}
              {{ line }}
            {% endif -%}
          {% else -%}
            {{ line }}
          {% endif -%}
        {% endfor -%}
      {% else -%}
        Kein Logfile geladen
      {% endif -%}
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: vertical-stack
    cards:
      - type: horizontal-stack
        cards:
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:plus
            size: 30%
            styles:
              card:
                - padding: 0px
                - width: 50px
                - height: 30px
            tap_action:
              action: perform-action
              service: input_number.increment
              service_data:
                entity_id: input_number.camera_logfile_lines
          - type: custom:button-card
            color_type: card
            color: white
            icon: mdi:minus
            size: 30%
            styles:
              card:
                - padding: 0px
                - width: 50px
                - height: 30px
            tap_action:
              action: perform-action
              service: input_number.decrement
              service_data:
                entity_id: input_number.camera_logfile_lines
          - type: custom:button-card
            icon: mdi:refresh
            name: Refresh
            size: 30%
            layout: icon_name
            tap_action:
              action: perform-action
              service: script.turn_on
              service_data:
                entity_id: script.om_scr_draussen_carport_kamera_refresh_logfile
            styles:
              card:
                - padding: 0px
                - width: 100px
              grid:
                - grid-template-areas: "\"i n\""
                - grid-template-columns: auto 1fr
              icon:
                - width: 30px
                - height: 30px
              name:
                - font-size: 15px
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
    grid_options:
      columns: 11
      rows: auto
  - type: markdown
    content: "#### Alarm temporär deaktivieren"
    title: s
    text_only: true
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
  - type: custom:mushroom-select-card
    entity: input_select.carport_kamera_alarm_aus_zeiten
    name: für ...
    icon: mdi:bell-off-outline
    fill_container: false
    icon_color: black
    primary_info: none
    secondary_info: none
    layout: vertical
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
    grid_options:
      columns: 6
      rows: 1
    card_mod:
      style: |
        ha-card {
          --ha-card-background: {% if is_state('timer.carport_kamera_alarm_aus_timer', 'active') %}red{% else %}transparent{% endif %};
        }
  - type: entities
    entities:
      - entity: timer.carport_kamera_alarm_aus_timer
        name: " "
        secondary_info: none
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "on"
    card_mod:
      style: |
        ha-card {
          --ha-card-background: {% if is_state('timer.carport_kamera_alarm_aus_timer', 'active') %}red{% else %}transparent{% endif %};
          color: white;
          font-weight: bold;
           --card-mod-icon-color: white;
        }
    grid_options:
      columns: 6
      rows: 1
  - type: markdown
    content: "### Kamera Carport ist aus."
    visibility:
      - condition: state
        entity: switch.carportkamerashelly_switch_0
        state: "off"
column_span: 1


Gutes Gelingen und Anregungen sind jederzeit willkommen!

PS: Ich muß jetzt unbedingt ins Bett sonst kriege ich morgen ach heute auf Arbeit nichts gebacken :wink:


EDIT1:
Nur ein bischen Kosmetik und im Markdown Code noch ein weiteres if/then eingebaut falls der MQQT Sensor aus irgendeinem Grund keinen Inhalt hat und auf Dashboard einen Fehler anzeigt.

1 „Gefällt mir“