Hack cámara Xiaomi dafang e integración en Home Assistant

Hace años tengo tres de estas cámaras en casa modificadas con un resultado estupendo , son unas cámaras con las tres BBB ( bueno , bonito y barato ) si se modifican y se les amplían sus funciones , eso si , haciendo esto no las podremos tener integradas en la aplicación MiHome de Xiaomi , pero al menos yo puedo prescindir de la aplicación oficial.

Aprovechando que una de las cámaras se estropeo por fallar el alimentador después de casi cinco años funcionando 24/7 me decidí a documentarlo ya que el proceso no es difícil , pero si un poco tedioso.

Lo primero es partir de una de esas cámaras , que seguramente vendrá con un firmware superior a las 5.5.1.194 , para ello tendremos que resetearlas y comprobar que versión de firmware tienen instalada , si es superior hay que hacer un proceso de downgrade.

La conectaremos a la aplicación MiHome buscando el modelo de cámara

La pondremos en modo de emparejamiento esperando a que arranque ( unos 20 segundos ) y pulsaremos tres segundos el botón de configuración.

Después tendremos que emparejarla con un código QR poniéndolo a unos 15 cms mas o menos , hay que moverlo un poco porque a veces no lo coge bien , cuando lo lee oiremos un mensaje en la camara ( en chino por supuesto )

El código QR hay que ponerlo a unos quince centímetros , pero hay que moverlo un poco para que lo lea bien , si lo le veremos que en chino nos dice una frase de confirmación , pulsaremos siguiente

Si todo es correcto nos dará el OK , si no aparece esta pantalla deberemos repetir el proceso anterior.

Una vez emparejada ya nos aparecerá en nuestra aplicación MiHome

Iremos a ajustes avanzados y veremos el firmware actual , en este caso tiene la 5.5.1.243 , por lo que haremos un downgrade a la versión 5.5.1.194

Para ello descargaremos el fichero que hay mas abajo y lo renombraremos a demo.bin , formatearemos una tarjeta uSD de como máximo 1GB o menos y lo copiaremos en ella

SI no tenéis una tarjeta con tan poca capacidad podéis hacer lo mismo que hice yo , eliminar las particiones de la micro sd y crear una partición de 512MB

Copiaremos el fichero en la micro SD , pulsaremos y mantenemos pulsado el botón de setup sin alimentar la cámara , alimentamos la cámara y mantenemos pulsado durante unos quince segundos , soltamos el botón y si todo es correcto deberían encenderse los led azul y amarillo mientras se hace el cambio de firmware en la cámara , si todo es correcto a los tres o cuatro minutos la cámara empezara a rotar un par de veces , en este momento podemos quitar la tarjeta y reiniciar la cámara , este proceso a veces no funciona y hay que hacerlo varias veces , entonces pasados unos minutos volvernos a hacerlo de nuevo hasta que la cámara rote al finalizar.

Volveremos a enlazar con la aplicación MiHome siguiendo los pasos anteriores y comprobaremos la versión actual de firmware.

En este caso podemos ver que la versión es la 5.5.1.194 con lo que ya podemos empezar a hacerle el hack a la camara

La pagina del proyecto del hack es https://github.com/EliasKotlyar/Xiaomi-Dafang-Hacks

Lo primero será cambiar el firmware oficial por este modificado que pongo mas abajo , seguiremos el mismo proceso que para hacer el downgrade

Estos son los pasos para el cambio de firmware que aparecen en la pagina del proyecto

  1. Format your microSD to FAT32. NTFS, EXFAT etc. won’t work. Try to use smaller older SD cards like 512 MB or create just a single primary 512 MB partition on it for maximum success rate.
  2. Copy the CFW-Binary from step 1 to the formatted microSD card and rename it to “demo.bin”. For Wyzecam v3 the filename must be demo_wcv3.bin. There must not be other files on the microSD! This is really important and it won’t work if there are any other files on there.
  3. Remove the power cable from the camera and plug the microSD card into the camera
  4. Hold down the setup button on the camera while
  5. Plugging in the USB power cable
  6. Keep the setup button pressed for another 10 seconds
  7. Wait until the firmware has finished flashing (like 3 minutes). You can disconnect the power as soon as the base starts moving (DaFang/ Wyzecam Pan).
  8. Remove the microSD card and power up the camera
  9. You should see the blue led shining for 5 seconds (not blinking) before the base starts moving (DaFang/ Wyzecam Pan). If not, something went wrong. You should try another microSD card and look at the community tips at the bottom of the page. Start over from step 1.

Prepararemos una tarjeta micro SD formateada , en mi caso de 32GB y copiaremos el contenido de este fichero en la raiz de la tarjeta

Deberá quedar algo similar a esto

E:/
├── autoupdate.sh
├── bin
├── config
├── controlscripts
├── driver
├── media
├── run.sh
├── scripts
├── uEnv.bootfromnand.txt
├── uEnv.bootfromsdcard.txt
├── uboot-flash
└── www

En la pagina del proyecto pone que copiemos el fichero config/wpa_supplicant.conf.dist a config/wpa_supplicant.conf y lo configuremos , yo aprovecho y antes de arrancar modifico estos ficheros para tenerla lista completamente nada mas arrancar :

  • hostname.conf
  • mqtt.conf
  • staticip.conf
  • timezone.conf
  • wpa_supplicant.conf

hostname.conf

Definimos el nombre de la camara

camara_trastero

mqtt.conf

Aquí configuramos toda la parte que tiene que ver con MQTT

############################################################
# edit this file and move it to /system/sdcard/config/mqtt.conf #
############################################################

export LD_LIBRARY_PATH='/thirdlib:/system/lib:/system/sdcard/lib'

# Options for mosquitto_sub & mosquitto_pub
USER=xxxxxxxxxxxx
PASS=yyyyyyyyyyyyyyyy
HOST=192.168.1.145
PORT=1883

# Define a location
LOCATION="trastero"

# Define device name
DEVICE_NAME="camara_trastero"

# Define the base topic used by the camera
# send a message to myhome/dafang/set with the payload help for help.
# Results will be placed in myhome/dafang/${command} or topic/dafang/error - so please subscribe topic/dafang/# for testing purposes
TOPIC="$LOCATION/$DEVICE_NAME"

# Define an autodiscovery prefix, if autodiscovery is desired:
# AUTODISCOVERY_PREFIX="homeassistant"

# Define additional options for Mosquitto here.
# For example --cafile /system/sdcard/config/DST_Root_CA_X3.pem --tls-version tlsv1
# or use a special id to connect to brokers like azure
MOSQUITTOOPTS=""

# Add options for mosquitto_pub like -r for retaining messages
MOSQUITTOPUBOPTS=""

# Send a mqtt statusupdate every n seconds
STATUSINTERVAL=30

# Define whether you would like to have light level exposed (by hardware sensor og virtual calcuations).
# If the device doesn't come with a hardware sensor, your only option is to use 'virtual'.
# 
#  Options:
#     hw = Use 'hw' if the device has build in hardware light sensor. For more details, see . See issue eg. #1120 for more details...
#     virtual = Use 'virtual' calculations, based on the lightlevel on the image, also known as ISP exposure (from /proc/jz/isp/isp_info)
#     false = Use 'false' (default) to DISABLE the topic (It will not publishing details about light level)
LIGHT_SENSOR="false"

staticip.conf

Si queremos una direccion ip fija como es mi caso la definimos aqui.

# IP address and subnet mask
# The format must be [ip] netmask [netmask]
# Ex: 10.0.0.10 netmask 255.255.255.0
192.168.1.82 netmask 255.255.255.0

timezone.conf

Configuración del huso horario

Europe/Madrid

wpa_supplicant.conf

Aquí definimos a que red wifi queremos conectarnos y con que credenciales

# For more configuration option please see:
# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
ap_scan=1

network={
        ssid="CarpeDiem"
        # Uncomment to connect to Hidden SSIDs
        #scan_ssid=1 
        key_mgmt=WPA-PSK
        pairwise=CCMP TKIP
        group=CCMP TKIP WEP104 WEP40
        psk="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        priority=2
}

Una vez arrancada iremos a la ip de la cámara y nos autenticaremos con las credenciales root e ismart12 , entraremos en la pagina web de configuración y podremos modificar la configuración a nuestro gusto

Como yo solo tenia que sustituir la probe desde el bot de Telegram que tengo en mi Home Assistant y todo perfecto

Empezamos por definir un sensor binario de movimiento

    - state_topic: 'trastero/camara_trastero/motion'
      name: 'trastero_dafang_movimiento'

Otro sensor binario de ping

  - platform: ping 
    host: 192.168.1.82
    name: ping_camara_xiaomi_trastero
    count: 5
    scan_interval: 60

Definimos una serie de sensores de información de la camara

    - name: "trastero_bitrate"
      # friendly_name: trastero Bitrate
      icon: "mdi:speedometer"
      state_topic: "trastero/camara_trastero"
      value_template: "{{ value_json.bitrate.split(' ')[0] }}"
      unit_of_measurement: "Mb/s"
  
    - name: "trastero_signal_level"
      # friendly_name: trastero Signal Level
      icon: "mdi:signal"
      state_topic: "trastero/camara_trastero"
      value_template: "{{ value_json.signal_level[:-1] }}"
      unit_of_measurement: "%"
  
    - name: "trastero_link_quality"
      # friendly_name: trastero Link Quality
      icon: "mdi:signal"
      state_topic: "trastero/camara_trastero"
      value_template: "{{ value_json.link_quality[:-1] }}"
      unit_of_measurement: "%"
  
    - name: "trastero_noise_level"
      # friendly_name: trastero Noise Level
      icon: "mdi:signal"
      state_topic: "trastero/camara_trastero"
      value_template: "{{ value_json.noise_level[:-1] }}"
      unit_of_measurement: "%"

Definimos una serie de switchs para la configuración desde HA de la cámara

    - name: "camara_trastero_led_infrarojo"
      # friendly_name: trastero LED Infrarojo
      icon: "mdi:led-variant-on"
      state_topic: "trastero/camara_trastero/leds/ir"
      command_topic: "trastero/camara_trastero/leds/ir/set"
      payload_on: "ON"
      payload_off: "OFF"
  
    - name: "camara_trastero_filtro_led_infrarojo"
      # friendly_name: trastero Filtro LED Infrarojo
      icon: "mdi:led-variant-on"
      state_topic: "trastero/camara_trastero/ir_cut"
      command_topic: "trastero/camara_trastero/ir_cut/set"
      payload_on: "ON"
      payload_off: "OFF"
  
    - name: "camara_trastero_led_azul"
      # friendly_name: trastero LED azul
      icon: "mdi:led-variant-on"
      state_topic: "trastero/camara_trastero/leds/blue"
      command_topic: "trastero/camara_trastero/leds/blue/set"
      payload_on: "ON"
      payload_off: "OFF"
      
    - name: "camara_trastero_led_amarillo"
      # friendly_name: trastero LED amarillo
      icon: "mdi:led-variant-on"
      state_topic: "trastero/camara_trastero/leds/yellow"
      command_topic: "trastero/camara_trastero/leds/yellow/set"
      payload_on: "ON"
      payload_off: "OFF"    
         
    - name: "camara_trastero_deteccion_movimiento"
      # friendly_name: trastero deteccion movimiento
      icon: "mdi:walk"
      state_topic: "trastero/camara_trastero/motion/detection"
      command_topic: "trastero/camara_trastero/motion/detection/set"
      payload_on: "ON"
      payload_off: "OFF"
  
    - name: "camara_trastero_night_mode"
      # friendly_name: trastero Night Mode
      icon: "mdi:weather-night"
      state_topic: "trastero/camara_trastero/night_mode"
      command_topic: "trastero/camara_trastero/night_mode/set"
      payload_on: "ON"
      payload_off: "OFF"
  
    - name: "camara_trastero_night_mode_auto"
      # friendly_name: trastero Night Mode auto
      icon: "mdi:weather-night"
      state_topic: "trastero/camara_trastero/night_mode/auto"
      command_topic: "trastero/camara_trastero/night_mode/auto/set"
      payload_on: "ON"
      payload_off: "OFF"
      
    - name: "camara_trastero_rtsp_h264"
      # friendly_name: Servidor RTSP h264
      icon: "mdi:video"
      state_topic: "trastero/camara_trastero/rtsp_h264_server"
      command_topic: "trastero/camara_trastero/rtsp_h264_server/set"
      payload_on: "ON"
      payload_off: "OFF"
  
    - name: "camara_trastero_rtsp_mjpeg"
      # friendly_name: Servidor RTSP MJPEG
      icon: "mdi:video"
      state_topic: "trastero/camara_trastero/rtsp_mjpeg_server"
      command_topic: "trastero/camara_trastero/rtsp_mjpeg_server/set"
      payload_on: "ON"
      payload_off: "OFF"  

Y en el customize lo dejamos a nuestro gusto

switch.camara_trastero_led_infrarojo:
  friendly_name: Trastero LED Infrarojo
  icon: mdi:led-variant-on
switch.camara_trastero_filtro_led_infrarojo:
  friendly_name: Trastero Filtro LED Infrarojo
  icon: mdi:led-variant-on
switch.camara_trastero_led_azul:
  friendly_name: Trastero LED azul
  icon: mdi:led-variant-on
switch.camara_trastero_led_amarillo:
  friendly_name: Trastero LED amarillo
  icon: mdi:led-variant-on
switch.camara_trastero_deteccion_movimiento:
  friendly_name: Trastero deteccion movimiento
  icon: mdi:walk
switch.camara_trastero_night_mode:
  friendly_name: Trastero Night Mode
  icon: mdi:weather-night
switch.camara_trastero_night_mode_auto:
  friendly_name: Trastero Night Mode auto
  icon: mdi:weather-night
switch.camara_trastero_rtsp_h264:
  friendly_name: Servidor RTSP h264
  icon: mdi:video
switch.camara_trastero_rtsp_mjpeg:
  friendly_name: Servidor RTSP MJPEG
  icon: mdi:video
sensor.trastero_bitrate:
  friendly_name: Trastero Bitrate
  icon: mdi:speedometer
sensor.trastero_signal_level:
  friendly_name: Trastero Signal Level
  icon: mdi:signal
sensor.trastero_link_quality:
  friendly_name: Trastero Link Quality
  icon: mdi:signal
sensor.trastero_noise_level:
  friendly_name: Trastero Noise Level
  icon: mdi:signal

Definimos la camara

  - platform: ffmpeg
    name: trastero_dafang
    input: !secret url_trastero_dafang
    # extra_arguments: -q:v 2-32

Ejemplo de script para el envio de imagenes

camara_trastero_imagen_nuevo:
  variables:
    filepath: /config/www/cam_captures/trastero_generic_{{now().year}}_{{now().month}}_{{now().day}}_{{now().hour}}_{{now().minute}}_{{now().second}}.jpg 
  alias: camara_trastero_imagen_nuevo
  sequence:
    - service: camera.snapshot
      data:
        entity_id: camera.trastero_generic
        filename: '{{ filepath }}'
    - service: notify.notif_telegram_bot
      data:
        title: Enviar imagenes
        message: Cam. trastero {{now().strftime("%d/%m %H:%M:%S")}}
        data:
          photo:
          - file: '{{ filepath }}'
            capture: Snapshoot
            caption: Cam. trastero {{now().strftime("%d/%m %H:%M:%S")}}
    - delay: 00:00:01 

Esta automatización por ejemplo si detecta movimiento sin estar nadie de la familia en casa empieza a enviar imagenes

- id: Camara Dafang trastero deteccion de movimiento 
  alias: Camara Dafang trastero deteccion de movimiento 
  initial_state: 'on'
  trigger:
    - platform: state
      entity_id: binary_sensor.trastero_dafang_movimiento 
      to: 'on'  
  # Alguien dentro no enviamos a pesar de la deteccion de movimiento    
  condition:
    condition: state
    entity_id: input_boolean.int_alguien_en_casa
    state: 'off'
  action:
    - service: notify.notif_telegram_bot
      data:
        message: "*Trastero* , Movimiento detectado" 
    #Enviamos las imagenes    
    - service: script.turn_on
      entity_id: script.camara_trastero_imagen_nuevo   
    - wait_template: "{{ is_state('script.camara_trastero_imagen_nuevo', 'off') }}"
      timeout: '00:00:30'
    - delay: 00:00:01

    - service: script.turn_on
      entity_id: script.camara_trastero_imagen_nuevo   
    - wait_template: "{{ is_state('script.camara_trastero_imagen_nuevo', 'off') }}"
      timeout: '00:00:30'
    - delay: 00:00:01

    - service: script.turn_on
      entity_id: script.camara_trastero_imagen_nuevo   
    - wait_template: "{{ is_state('script.camara_trastero_imagen_nuevo', 'off') }}"
      timeout: '00:00:30'
    - delay: 00:00:01

    - service: script.turn_on
      entity_id: script.camara_trastero_imagen_nuevo   
    - wait_template: "{{ is_state('script.camara_trastero_imagen_nuevo', 'off') }}"
      timeout: '00:00:30'
    - delay: 00:00:01

    - service: script.turn_on
      entity_id: script.camara_trastero_imagen_nuevo   
    - wait_template: "{{ is_state('script.camara_trastero_imagen_nuevo', 'off') }}"
      timeout: '00:00:30'
    - delay: 00:00:01

Esta nos avisaria por Telegram

- alias: Si la puerta del trastero se abre y no hay nadie en casa envia aviso de ello por telegram
  initial_state: 'on'
  trigger:
    - platform: state
      entity_id: binary_sensor.sensor_trastero_evento
      from: 'off'
      to: 'on'
  condition:
    condition: state
    entity_id: input_boolean.int_alguien_en_casa
    state: 'off'  
  action:
    # - delay: 00:00:03     
    - service: notify.notif_telegram_bot
      data:
        message: "*Puerta trastero abierta y nadie en casa*"
    - service: notify.notif_telegram_ha_urgentes
      data:
        message: "*Puerta trastero abierta y nadie en casa*"

Quedara un panel mas o menos asi

Y con esto un bizcocho ….