Sinilink WIFI USB Controller XY-WFUSB (turns USB port power on/off) - ESPHome YAML

This device allows you to turn the USB device connected to it on and off. It supports data pass through however my main uses are just to turn USB devices, such as a USB Microscope, on and off.

I found some very basic YAML online so I figured I’d create my own. Sharing in case others would like to use it. I split the YAML into two so that you don’t need to edit many files if you make changes to the core functionality in the template file.

While you can use the restore_mode to define the USB port state at boot up, I wanted to be able to configure it so I set it to ALWAYS_OFF and then switch it on if the configured default is On. If it isn’t, then it will switch it off at boot… this is not necessary unless you change the restore_mode to something that may default it to on.

The Red led is wired to the output mosfet that controls power to the USB port so it can’t be configured and will always turn on when the port is on. The green and blue leds are configurable however I let the blue led remain the status_led as I had no other function for it. The green led turns on when the port is off to indicate there is power, and off when the port is on and thus the red led is on. You can now also configure it to be on when the port is on (along with the red), or just disable it to minimize light output & power draw.

If you want to merge all the YAML into one file, remember to remove the extra indent on the YAML in the template file.

I am not a programmer so I am quite sure there are better ways to accomplish what I did… glad to get feedback on how to improve the YAML.

EDIT: For those who will likely tell me that I could have avoided lambda… I used it as it is more compact and IMO versatile. Boils down to personal preference.

microscope.yaml

substitutions:
  devicename: microscope
  devicename_no_dashes: microscope
  friendly_devicename: "Microscope"
  device_description: "Microscope"
  update_interval_wifi: "120s"
  restore_mode_setting: ALWAYS_OFF
  #restore_mode: Control how the relay attempts to restore state on bootup.
  #RESTORE_DEFAULT_OFF          - Attempt to restore state and default to OFF if not possible to restore.
  #RESTORE_DEFAULT_ON           - Attempt to restore state and default to ON.
  #RESTORE_INVERTED_DEFAULT_OFF - Attempt to restore state inverted from the previous state and default to OFF.
  #RESTORE_INVERTED_DEFAULT_ON  - Attempt to restore state inverted from the previous state and default to ON.
  #ALWAYS_OFF                   - Always initialize the pin as OFF on bootup.
  #ALWAYS_ON                    - Always initialize the pin as ON on bootup.

esphome:
  name: "${devicename}"
  friendly_name: "${friendly_devicename}"
  comment: "${device_description}"
  on_boot:
    then:
      - script.execute: on_boot_default_state

esp8266:
  board: esp01_1m
  framework:
    version: recommended
  restore_from_flash: true

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename}"
    password: !secret iot_wifi_password
    ap_timeout: 3min

#Faster than DHCP. Also use if can't reach because of name change
  #manual_ip:
    #static_ip: 192.168.3.218
    #gateway: 192.168.3.1
    #subnet: 255.255.255.0
    #dns1: 192.168.1.25
    #dns2: 192.168.1.26

#Manually override what address to use to connect to the ESP.
#Defaults to auto-generated value. Example, if you have changed your
#static IP and want to flash OTA to the previously configured IP address.
  #use_address: 192.168.3.218


<<: !include usb-port-switch-template.yaml

usb-port-switch-template.yaml


  logger:
    logs:
      sensor: INFO
      binary_sensor: INFO
      text_sensor: INFO

  # Enable Home Assistant API
  api:

  ota:
    - platform: esphome
      password: !secret ota_pwd

  web_server:
    port: 80
    version: 2
    include_internal: true
    ota: false

  captive_portal:

  # Sync time with Home Assistant
  time:
    - platform: homeassistant
      id: ha_time

  text_sensor:
    - platform: wifi_info
      ip_address:
        name: "IP"
        icon: "mdi:ip-outline"
        update_interval: ${update_interval_wifi}
      ssid:
        name: "SSID"
        icon: "mdi:wifi-settings"
        update_interval: ${update_interval_wifi}
      bssid:
        name: "BSSID"
        icon: "mdi:wifi-settings"
        update_interval: ${update_interval_wifi}
      mac_address:
        name: "MAC"
        icon: "mdi:network-outline"
      scan_results:
        name: "Wifi Scan"
        icon: "mdi:wifi-refresh"
        disabled_by_default: true

  #Alternative, if blue led is needed for something else:
  #https://esphome.io/components/light/status_led.html
  status_led:
    pin:
      number: 16 # Blue LED

  switch:
    - platform: gpio
      id: port_power
      restore_mode: ${restore_mode_setting}
      pin: 5

    - platform: gpio
      pin: 14
      id: green_led
      inverted: false

    - platform: template
      id: usb_port
      name: ""
      lambda: |-
        if (id(port_power).state) {
          return true;
        } else {
          return false;
        }
      turn_on_action:
        - lambda: |-
            id(port_power).turn_on();
      turn_off_action:
        - lambda: |-
            id(port_power).turn_off();
      on_turn_on:
        - lambda: |-
            if (id(green_led_mode).state != "Never" && id(green_led_mode).state != "Always") {
              if (id(green_led_mode).state == "Port On") {
                id(green_led).turn_on();
                }
                else {
                  id(green_led).turn_off();
                }
            }
      on_turn_off:
        - lambda: |-
            if (id(green_led_mode).state != "Never" && id(green_led_mode).state != "Always") {
              if (id(green_led_mode).state == "Port On") {
                id(green_led).turn_off();
                }
                else {
                  id(green_led).turn_on();
                }
            }
      
  # Button
  binary_sensor:
    - platform: gpio
      id: btn
      name: "Button"
      pin:
        number: 4
        inverted: true
        mode:
          input: true
          pullup: true
      on_press:
        - switch.toggle: port_power

  sensor:
    - platform: wifi_signal
      name: "WiFi Signal"
      update_interval: ${update_interval_wifi}
      device_class: signal_strength

  button:
    - platform: restart
      name: "Restart"
      entity_category: diagnostic

    - platform: safe_mode
      name: "Safe Mode"
      entity_category: diagnostic

  select:
      # option to control how LEDs are used. Red led is hardwaired to output so it cannot be controlled
    - platform: template
      name: "Green LED ON when"
      icon: mdi:led-on
      id: green_led_mode
      optimistic: true
      options:
        - "Port On"
        - "Port Off"
        - "Always"
        - "Never"
      initial_option: "Port Off"
      restore_value: true
      entity_category: config
      on_value:
        - lambda: |-
            if (id(green_led_mode).state == "Always") {
              id(green_led).turn_on();
            }
            if (id(green_led_mode).state == "Never") {
              id(green_led).turn_off();
            }
            if (id(green_led_mode).state == "Port On" && id(usb_port).state) {
              id(green_led).turn_on();
            }
            if (id(green_led_mode).state == "Port On" && !id(usb_port).state) {
              id(green_led).turn_off();
            }
            if (id(green_led_mode).state == "Port Off" && id(usb_port).state) {
              id(green_led).turn_off();
            }
            if (id(green_led_mode).state == "Port Off" && !id(usb_port).state) {
              id(green_led).turn_on();
            }

      # option to disable button
    - platform: template
      name: "Default Port State"
      id: default_state
      optimistic: true
      options:
        - "On"
        - "Off"
      initial_option: "On"
      restore_value: true
      entity_category: config

  script:
    - id: on_boot_default_state
      then:
        lambda: |-
              if(id(default_state).state == "On") {
                id(port_power).turn_on();
              } else {
                id(port_power).turn_off();
              }

Edit: I added the platform under OTA so it works for ESPHome 2024.6+

3 Likes

Thank you for the config! It works very well and precisely how I wanted.

A few notes that could help others. Pinout can be found here: Sinilink USB Switch Module (XY-WFUSB) Configuration for Tasmota

Connection holes on the USB controller are small, but if you have some UTP Cat5 cable laying around, it could do the trick.

Connection from the USB controller to a programmer:

  • GNG → GND
  • TX → RX
  • RX → TX
  • IO0 → GND
  • RST (not connected)
  • 3v3 → 3v3

Thanks!

@michal_p - Good idea to reference pinout. To avoid issues if the link to the source were to change, here is a snapshot. As you also noted, TX and RX must be crossed with connecting to the UART to USB adapter.

To connect to the tiny pin holes I used clothing pins stuck into wire jumpers I cut. The jumpers were connected to the USB to UART adapter. Using clothing pins allows you to hold them in place without injury. Mine had a plastic bead at the top so that also worked well as a spacer. This is a crude method but works if nothing better is available. I had not thought about the solution you used, I’ll keep it in mind next time!

I’ve also tried using pins, but I’ve only found ones that were too big.

UTP cat5 cable has a good cross section. I shaved a bit of the cable insulation and the wire went into the hole well. Then I just bent the end of the wire around the hole and it held really nicely. :slight_smile:

Anyhow, many thanks for the template, it works great!

2 Likes

I tried to use small, solid-core wire, but it was too big for those holes. I tried to use tiny pogo pins, but they were also too big. So I ended up using scraps of Cat5 cables, and that worked great! Thanks for the suggestion, @michal_p!!

2 Likes

Since this appears to be a common issue, this is what works great for me:

The ball tip is critical as it acts as a spacer and prevents the pins touching each other, plus it is easy on the hands :slight_smile: . I did not use a single connector on the adapter side as this was meant to be a temporary solution but having worked well on this device and a few others I kept it as is. To hold them in place I position one by one and hold everything with one hand. The adapter is plugged into a short extension cable so that it is easier for me to plug the adapter in when ready to flash the device. Mock up picture:

Of course, if I had to do this for 100 units I’d get annoyed by it quickly, but to do one every so often it is quite easy and versatile.

BTW, this DSD TECH SH-U09C5 USB to TTL UART adapter with FTDI Chip is awesome. It also supports 5V 3.3V 2.5V 1.8V and has a few extra pina on the top, of which GND was the most useful up to now. I have flashed around 30 to 40 espHome devices using it.

2 Likes