Skip to main content

Primary links

  • Home
  • AI
  • Kubernetes
  • Incus
  • Ansible
  • Terraform
  • OpenStack
  • Virtualization
  • Linux
  • SmartHome
  • HowTo

Misc

  • Linux
  • Hardware
    • APC Back-UPS ES 700G
    • ARM
    • AVM
    • AX88772A Fast Ethernet USB Adapter
    • AXIS M1144-L
    • Adaptec RAID controller
    • Android
    • Archive
    • Arduino
    • Bionx
    • Bluetooth
    • Brother
    • Cisco
    • DCPMM / NVRAM
    • Dell
    • ESP32 / ESP8266
      • DPM8624 modbus control with esp8266
      • Sinilink XY6014 modbus control with ESP8266 ESPhome
      • Sonoff BASIC R1 with ESPhome
    • HP (Hewlett Packard)
    • IPMI
    • Intel NVMe
    • LSI MegaRAID
    • Lenovo ThinkPad
    • Linpack
    • Linux Benchmark
    • Logitech Squeezebox Touch
    • Logitech
    • Mellanox
    • Micron
    • NVMe
    • OpenWRT
    • RaLink RT2500
    • Raspberry Pi
    • SmartHome
    • Snom 320 / 370
    • Solar
    • Sony KDL-55EX725
    • Supermicro
    • Surface
    • TEMPer (usb temperatur)
    • USB Serial adapter
    • Ubiquiti
    • Update Intel NIC firmware
    • Yamaha
    • ZyXEL NSA320
    • ahoy DTU
    • batteries
    • hdparm
    • iDRAC
    • memtest86+
    • smartmontools
  • Programming
  • Databases
  • CLI
  • Multimedia
  • Plugins
  • Windows

Cloud

  • OpenStack
  • cloud-config
  • nextcloud

Virtualization

  • Virtualization
  • Docker
  • KVM
  • Kubernetes
  • LXC
  • LXD
  • QEMU
  • VMware
  • VirtualBox
  • multipass
  • podman
  • vagrant
  • XEN
  • Incus

Network

  • DNS
  • Firewall
  • Linux
  • OpenvSwitch
  • SSL
  • VLAN
  • VPN
  • iPXE
  • namespaces
  • nmcli
  • tcpdump

Storage

  • CEPH
  • DRBD
  • LVM
  • S3
  • ZFS
  • btrfs

Automation / CI/CD

  • Install
  • Ansible
  • GitLab
  • LLM
  • Preseed
  • Puppet
  • Terraform
  • Ubuntu autoinstall

Monitoring / Visualisation

  • Grafana
  • Icinga
  • Prometheus
  • Monitoring
  • ELK
  • mermaid
  • esp8266
  • esphome

ESPhome configuration

substitutions:
  # Change this model to fit your particular one.
  # You can find it in Home Assistant as the device diagnostic "Model Name".
  model: "XY6014"
  device_name: "xy6014-controller"
  device_friendly_name: "sinilink-XY6014"
  device_description: "Power Supply"

  # Model specific settings (Don't change these!)
  XY6014_voltage_maximum: "60"
  XY6014_voltage_accuracy: "2"
  XY6014_voltage_multiplier: "0.01"
  XY6014_current_maximum: "14"
  XY6014_current_accuracy: "3"
  XY6014_current_multiplier: "0.01"

globals:
   - id:   vin_undervoltage_theshold
     type: float
     restore_value: no
     initial_value: '17'

esphome:
  name: $device_name
  friendly_name: $device_friendly_name
  comment: $device_description
  name_add_mac_suffix: false
  project:
    name: "sinilink.xy5008-controller"
    version: "1.0.0"

esp8266:
  board: d1_mini

# Enable logging
logger:
  level: INFO
  # Disable logging via UART, since we're using this for modbus communication
  baud_rate: 0

# Enable status LED
status_led:
  pin:
    number: GPIO2
    inverted: true

web_server:
  port: 80
  local: true
  log: false

# Enable Home Assistant API
api:
#  encryption:
#    key: !secret home_assistant_key
  reboot_timeout: 0s

ota:
  - platform: esphome
    #password: !secret ota_password

# Configure WiFi
wifi:
  networks:
  - ssid: !secret wifi_ssid
    password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "XY6014 Fallback Hotspot"
    password: ""

text_sensor:
  - platform: wifi_info
    ip_address:
      name: ESP IP Address
      id: ip_address
      on_value:      
        then:
          - lambda: |-

  - platform: template
    name: "Turn On Time"
    lambda: |-
      char buffer[16];
      std::string ton_time;
      std::snprintf(buffer, 16, "%02d:%02d:%02d" , (int)id(turnon_hours).state, (int)id(turnon_minutes).state, (int)id(turnon_seconds).state);
      ton_time = buffer;

      return {ton_time};
    update_interval: 1s

interval:
  - interval: 5s
    then:
      - lambda: |-
          char ip[18];
          char *ptr =ip;

          strncpy(ip,id(ip_address).state.c_str(),17);
          ip[17]=0;
          int ip1 = atoi(ptr);
          ptr = strchr( ptr, '.');
          if (ptr!=NULL) {
            int ip2 = atoi(++ptr);
            ptr = strchr( ptr, '.');
            if (ptr!=NULL) {
              int ip3 = atoi(++ptr);
              ptr = strchr( ptr, '.');
              if (ptr!=NULL) {
                int ip4 = atoi(++ptr);
                esphome::modbus_controller::ModbusController *controller = id(powersupply);
                // create the payload
                std::vector wifi_data = { 0x3b3a, 2, 4, uint16_t((ip1 << 8) | ip2), uint16_t((ip3 << 8) | ip4) };
                // Create a ModBUS command item with the wifi information as the payload
                esphome::modbus_controller::ModbusCommandItem set_wifi_command =
                      esphome::modbus_controller::ModbusCommandItem::create_write_multiple_command(controller, 0x30, 5, wifi_data);
                // Submit the command to the send queue
                powersupply->queue_command(set_wifi_command);
              }
            }
          }

  - interval: 1s
    then:
      - lambda: |-
          esphome::modbus_controller::ModbusController *controller = id(powersupply);
          esphome::modbus_controller::ModbusCommandItem set_wifi_command =
              esphome::modbus_controller::ModbusCommandItem::create_read_command(controller, esphome::modbus_controller::ModbusRegisterType::HOLDING, 0x30, 5);
          // Submit the command to the send queue
          powersupply->queue_command(set_wifi_command);

  - interval: 0.5s
    then:
      - lambda: |-
          static int count = 0;
          static int delay_count = 0;
          static int uv_count = 0;

          if ((id(output_switch).state) && (id(cutoff_current).state!=0) && (id(output_current).state < id(cutoff_current).state)){
            if (count>=5) {
              count = 0;
              id(output_switch).turn_off();
              id(out_relay).turn_off();
            }
            else count++;              
          }
          else count=0;

          if (id(input_voltage).state < id(vin_undervoltage_theshold)) { if (uv_count<4) uv_count++; }
          else {
             if (id(output_switch).state) id(out_relay).turn_on();
             uv_count=0;
          }

          if (uv_count >=4) {
              id(out_relay).turn_off();
          }    

          if (id(out_relay).state) {
              if (id(output_switch).state) delay_count=0;
              else delay_count++;
          }
          else delay_count=0;

          if (delay_count>=120*3) { 
              id(out_relay).turn_off();
              delay_count=0;
          }    

captive_portal:

uart:
  id: mod_bus
  tx_pin: GPIO1
  rx_pin: GPIO3
  baud_rate: 115200
  data_bits: 8
  stop_bits: 1
  parity: none

modbus:
 id: modbus1
# send_wait_time: 1ms

modbus_controller:
  - id: powersupply
    ## This address should be set to the "Address" value in the config menu
    address: 0x01
    modbus_id: modbus1
    setup_priority: 600
    update_interval: 1s
    command_throttle: 1ms

time:
  - platform: homeassistant
    id: ha_time


sensor:
  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 2
    name: "Output voltage"
    id: output_voltage
    device_class: voltage
    state_class: measurement
    unit_of_measurement: "V"
    register_type: holding
    value_type: U_WORD
    accuracy_decimals: ${${model}_voltage_accuracy}
    filters:
      - multiply: ${${model}_voltage_multiplier}

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 3
    name: "Output current"
    id: output_current
    device_class: current
    state_class: measurement
    unit_of_measurement: "A"
    register_type: holding
    value_type: U_WORD
    accuracy_decimals: ${${model}_current_accuracy}
    filters:
      - multiply: ${${model}_current_multiplier}

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 4
    name: "Output Power"
    device_class: power
    state_class: measurement
    unit_of_measurement: "W"
    register_type: holding
    value_type: U_WORD
    accuracy_decimals: 2
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 5
    name: "Input voltage"
    id: input_voltage
    device_class: voltage
    unit_of_measurement: "V"
    register_type: holding
    value_type: U_WORD
    accuracy_decimals: ${${model}_voltage_accuracy}
    #accuracy_decimals: 3
    filters:
    #  - multiply: ${${model}_voltage_multiplier}
      - multiply: 0.01

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 6
    name: "Battery charge"
    device_class: "energy_storage"
    state_class: measurement
    unit_of_measurement: "Ah"
    icon: "mdi:battery-60"
    register_type: holding
    value_type: U_DWORD_R
    accuracy_decimals: 3
    filters:
      - multiply: 0.001

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 8
    name: "Battery energy"
    device_class: "energy_storage"
    state_class: measurement
    unit_of_measurement: "Wh"
    icon: "mdi:battery-60"
    register_type: holding
    value_type: U_DWORD_R
    accuracy_decimals: 3
    filters:
      - multiply: 0.001

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 10
    name: "turnon_hours"
    id: turnon_hours
    unit_of_measurement: "hours"
    register_type: holding
    value_type: U_WORD
    internal: true

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 11
    name: "turnon_minutes"
    id: turnon_minutes
    unit_of_measurement: "minutes"
    register_type: holding
    value_type: U_WORD
    internal: true

  - platform: modbus_controller
    modbus_controller_id: powersupply
    address: 12
    name: "turnon_seconds"
    id: turnon_seconds
    unit_of_measurement: "seconds"
    register_type: holding
    value_type: U_WORD
    internal: true

  - platform: modbus_controller
    name: "Temperature"
    device_class: temperature
    state_class: measurement
    modbus_controller_id: powersupply
    register_type: holding
    address: 13
    value_type: S_WORD
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    name: "Temperature external"
    state_class: measurement
    modbus_controller_id: powersupply
    register_type: holding
    address: 14
    value_type: S_WORD
    device_class: temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1

  - platform: modbus_controller
    name: "Host type"
    state_class: measurement
    modbus_controller_id: powersupply
    register_type: holding
    address: 48
    value_type: U_WORD
    internal: true



  - platform: template
    name: "Battery Charge Power"
    id: battery_power
    unit_of_measurement: "W"
    device_class: "power"
    state_class: "measurement"
    accuracy_decimals: 1
    lambda: |-
      if (id(output_current).state > 0.05) {
        return id(output_voltage).state * id(output_current).state;
      } else {
        return 0.0;
      }

  - platform: total_daily_energy
    name: "Battery Charge Today"
    power_id: battery_power
    unit_of_measurement: "Wh"
    device_class: "energy"
    state_class: "total_increasing"
    accuracy_decimals: 0
    restore: false


binary_sensor:
  - platform: modbus_controller
    modbus_controller_id: powersupply
    name: "Constant Voltage"
    address: 17
    register_type: holding
    bitmask: 0x1
    filters:
      - invert:

  - platform: modbus_controller
    modbus_controller_id: powersupply
    name: "Constant Current"
    address: 17
    register_type: holding
    bitmask: 0x1 

number:
  - platform: modbus_controller
    modbus_controller_id: powersupply
    name: "Set output voltage"
    device_class: voltage
    unit_of_measurement: "V"
    entity_category: config
    mode: box
    address: 0
    value_type: U_WORD
    min_value: 0
    max_value: ${${model}_voltage_maximum}
    step: ${${model}_voltage_multiplier}
    lambda: !lambda return x * ${${model}_voltage_multiplier};
    write_lambda: !lambda return x * (1/${${model}_voltage_multiplier});

  - platform: modbus_controller
    modbus_controller_id: powersupply
    name: "Set output current"
    device_class: current
    unit_of_measurement: "A"
    entity_category: config
    mode: box
    address: 1
    value_type: U_WORD
    min_value: 0
    max_value: ${${model}_current_maximum}
    step: ${${model}_current_multiplier}
    lambda: !lambda return x * ${${model}_current_multiplier};
    write_lambda: !lambda return x * (1/${${model}_current_multiplier});

  - platform: template
    name: "Under current cutoff (battery charge)"
    id: cutoff_current
    device_class: current
    unit_of_measurement: "A"
    entity_category: config
    mode: box
    min_value: 0
    max_value: ${${model}_current_maximum}
    step: ${${model}_current_multiplier}
    optimistic: true

switch:
  - platform: modbus_controller
    modbus_controller_id: powersupply
    name: "Output"
    id: output_switch
    address: 18
    register_type: holding
    bitmask: 0x1
    entity_category: config

  - platform: gpio
    id: out_relay
    name: "Output relay"
    pin:
      number: GPIO4
      inverted: false

Flash

esphome run sinilink-modbus.yaml --device /dev/ttyUSB0
Profiles GitHub StackOverflow LinkedIn Xing
Contact Imprint
© panticz 2026

Cookie-Einstellungen

Diese Website nutzt eingebettete Inhalte von Drittanbietern (z.B. YouTube, SoundCloud). Beim Laden dieser Inhalte werden Daten an die jeweiligen Anbieter übermittelt. Datenverarbeitungserklärung