Cloning gpio_switch generates error in esphome::Application

I am trying to create my own external component and so I’m 'started small" (or so I thought!). I have cloned the standard ‘GPIO switch’ code (making suitable changes with the namespace and class name etc.).

I the YAML file I can create a yaml file:

switch:
  - platform: gpio
    pin: 
      number: GPIO21
      inverted: true
    id: test_led
    name: "Test LED"

and all works well.

I have edit the yaml file to be:

external_components:
  - source:
      type: local
      path: /config/My_Components
    components: [Lucci_Fan]

Lucci_Fan:
  id: test_led2
  name: "Test LED2"
  pin: 
    number: GPIO21
    inverted: true

and it tries to compile everything until it comes to the error:

src/main.cpp: In function 'void setup()':
src/main.cpp:181:7: error: 'class esphome::Application' has no member named 'register_switch'; did you mean 'register_component'?
   App.register_switch(test_led2);
       ^~~~~~~~~~~~~~~
       register_component
*** [.pioenvs/tester/src/main.cpp.o] Error 1

I understand that this is a file that is auto generated from the following files:
“__init__.py”

import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import switch
from esphome.const import (
    CONF_ID,
    CONF_PIN,
)

lucci_fan_ns = cg.esphome_ns.namespace("lucci_fan_")
Lucci_Fan = lucci_fan_ns.class_("Lucci_Fan", switch.Switch, cg.Component)


CONFIG_SCHEMA = (
    switch.switch_schema(Lucci_Fan)
    .extend(
        {
            cv.Required(CONF_PIN) : pins.gpio_output_pin_schema,
        }
    )
    .extend(cv.COMPONENT_SCHEMA)
)


async def to_code(config):
    var = await switch.new_switch(config)
    await cg.register_component(var, config)
    
    pin = await cg.gpio_pin_expression(config[CONF_PIN])
    cg.add(var.set_pin(pin))

“lucci_fan.h”:

#pragma once

#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "/esphome/esphome/components/switch/switch.h"


namespace esphome {
namespace lucci_fan_ {

class Lucci_Fan : public switch_::Switch, public Component {
 public:
  void setup() override;

  void dump_config() override;

  void set_pin( GPIOPin *pin_number);

 protected:
  virtual void write_state(bool state);

  GPIOPin *led_pin_;
};

}  // namespace lucci_fan_
}  // namespace esphome

“lucci_fan.cpp”:

#include "lucci_fan.h"
#include "esphome/core/log.h"

namespace esphome {
namespace lucci_fan_ {

static const char *const TAG = "lucci_fan";

void Lucci_Fan::setup()
{
	ESP_LOGCONFIG(TAG, "Lucci Fan Setup : '%s'", this->name_.c_str());
	
	bool initial_state = this->get_initial_state_with_restore_mode().value_or(false);
	if( initial_state)
	{ this->turn_on(); }
	else
	{ this->turn_off(); }
	
	this->led_pin_->setup();
	
	// Do this again for other IOs (???)
	if( initial_state)
	{ this->turn_on(); }
	else
	{ this->turn_off(); }
}

void Lucci_Fan::dump_config()
{
	LOG_SWITCH(TAG, "Lucci Fan Output:", this);
	LOG_PIN("  Pin: ", this->led_pin_);
}

void Lucci_Fan::write_state( bool state)
{
  ESP_LOGI(TAG, "'write state %s", ONOFF(state));
  this->led_pin_->digital_write( state);
  this->publish_state(state);
}

void Lucci_Fan::set_pin( GPIOPin *pin_number)
{
	this->led_pin_ = pin_number;
	ESP_LOGI(TAG, "GPIO pin is %d", this->led_pin_);
}

}  // namespace lucci_fan_
}  // namespace esphome

I cannot for the life of me see why that error is generated for my code but not for the GPIO Switch files.

Please let me know what I’m doing wrong?

Susan

looks like you chose a not simple thing to copy.

gpio is a platform not a component
So you are using platform code as a component, which doesn’t work, as you discovered.

What are you really trying to do, or what type of component are you trying to create?

At this stage I simply want to have a switch that will be sent to the ESP32.
Later on I will add a numerical value (between -1 and 15) and also a ‘value’ that will take one of 4 states.
I have already written the code to take those values to control a fan (the switch if for the light, the numeric value is the ‘address’ and the states are off, low, medium and high.
If these are set by the HA UI then they will be converted to the appropriate command bitstream to be sent to the fan via RF from an external circuit. If the remote control for the fan is used then the external circuit will pass the ‘listened to’ bitstream and decode it to the appropriate light or fan state. This wil then be also passed to the HA UI.
All that code is written and working - all I’m trying to do is to interface it back to the HA.

All that being said, what is there in the documentation or the code that is telling me that what I’ve based the above code on is for a platform and not a component? (Like many others according to the posts in these forums, I really don’t understand platform vs component vs whatever else.)

Finally, I have previously written what I thought was a component that was based on the ‘Pulse Counter’ component but instead of looking to a GPIO port, it received the pulses from another ‘binary sensor’ component. That seemed to work before and it was built in a very similar way. But I’m guessing that this will now be broken because of some change in the ESPHome framework. (It currently works so I really don’t want to touch it in case it stops!)

The documentation on all of this is “lacking” unless you are already very familiar with HA and esphome it seems.

From the documentation:

The gpio switch platform allows you to use any pin on your node as a switch.

The word platform is your clue. That and the way it is specified in the YAML, with the word platform.

Maybe if you specify it this way it will work:

switch:
  - platform: Lucci_fan
    pin: 
      number: GPIO21
      inverted: true
    id: test_led
    name: "Test LED"

I am not sure that will actually get you what you want later. I would look here first: Template Fan — ESPHome

That seems to get what you want in HA and then you should be able to interface it to your code to do the control. The tuya fan might be worth looking at too.

Thanks for the support @neel-m
That has not solved the original problem (the error message) but it did get me thinking last night and I think I may now have this ‘platform’ concept.
In my case I have a device that has 3 functions - a light to turn on and off. 1 3 speed (plus ‘off’) fan and a 4 bit address for the RF bitstream. Therefore ‘Lucci_Fan’ is the platform and there will be 3 components under that.
Taking your suggestion for the first part - the ‘switch’ for the light - I needed to alter the file structure as seen by ESPHome a bit.
It becomes:

Lucci_Fan
   __init__.py
  switch
    __init__.py
    lucci_fan.cpp
    lucci_fan.h

(Obviously I’ll need to change the names of the .cpp and.h files but this is a first step.)
Given that folder structure, what you suggested for the yaml file works beautifully. This also can be extended later for the other functions.
However, as I said, this still gives the same error when compiling.
One step forward…
Susan

OK - I think I’m getting somewhere.
I found the ‘demo’ component that simply creates a whole lot of entities and I’ve cut that down to the 3 that I need (2 numbers and a switch) and they now show up on the ‘device’ as entities that I can manipulate.
Therefore the next thing is to start connecting these to the interface code that I already have to the other hardware n the board.
And that is a whole different story.
Just writing tis to close off in case anyone else starts to go down the same path that I did.
Susan

1 Like