Compile problem with custom component and register_service()

I am writing a custom application in a .h file to use with the esp8266-based $4 ESP8266 XY_Clock on Aliexpress, but I’m having difficulty getting register_service to compile properly. I’m following the example in the documentation (Washing machine cycle example) with changes to the argument names and types.

Here is the example code snippet from the documentation:

// Declare a second service "start_washer_cycle"
    //  - Service will be called "esphome.<NODE_NAME>_start_washer_cycle" in Home Assistant.
    //  - The service has three arguments (type inferred from method definition):
    //     - cycle_duration: integer
    //     - silent: boolean
    //     - string_argument: string
    //  - The function start_washer_cycle declared below will attached to the service.
    register_service(&MyCustomComponent::on_start_washer_cycle, "start_washer_cycle",
                     {"cycle_duration", "silent", "string_argument"});

The compile error I’m getting is:

In file included from src/main.cpp:32:
src/xy_clock.h: In member function 'virtual void XY_Clock::setup()':
src/xy_clock.h:90:5: error: 'register_service' was not declared in this scope
   90 |     register_service(&XY_Clock::set_alarm_time, "alarm_time_set",

Here is the source code for the custom application (xy_clock.h):

#include "esphome.h"

#define TM1650_DIGIT_BASE 0x34                                      // Address of the left-most digit 
#define TM1650_DCTRL_BASE   0x24                                    // Address of the control register of the left-most digit
#define TM1650_ENABLE_BIT_ONOFF	0b00000001                          // Digit enable on/off bit positon
#define TM1650_MASK_ONOFF	((~TM1650_ENABLE_BIT_ONOFF) & 0xFF)       // Digit on/off bit mask
#define TM1650_BIT_DOT		0b00001000                                // Digit dot on/off bit position
#define TM1650_MASK_DOT		((~TM1650_BIT_DIT) & 0xFF)                // Digit dot on/off mask
#define TM1650_BRIGHT_SHIFT	4                                       // Number of bit shifts for brightness bits
#define TM1650_MASK_BRIGHT	0b10001111                              // Brightness bit mask
#define TM1650_MIN_BRIGHT	0                                         // Minimum brightness
#define TM1650_MAX_BRIGHT	7                                         // Maximum brightness

#define STATE_INIT          0                                       // State set at power on
#define STATE_HW_INIT       1                                       // State when initializing display hardware
#define STATE_DISPLAY_TIME  2                                       // Display time when in this state

#define TAG "xy-clock"


class XY_Clock : public PollingComponent {
 private:
  homeassistant::HomeassistantTime *hass_time;
  sntp::SNTPComponent *sntp_time;
  ds1307::DS1307Component *ds1307_time;

  int state;                                                        // State variable
  bool hw_clock_set;                                                // Hardware clock set if true
  uint8_t ctrl_save[4];                                             // Local storage for TM1650 control register values
  uint8_t digit_save[4];                                            // Local storage for digits
  char digits[5];                                                   // Storage for ascii time digits                                                                               

 public:
  // constructor: Set 500mS polling time
  XY_Clock(homeassistant::HomeassistantTime *to_hass, sntp::SNTPComponent *tc_sntp, ds1307::DS1307Component *tc_ds1307 ) : PollingComponent(1000) {
    hass_time = to_hass;
    sntp_time = tc_sntp;
    ds1307_time = tc_ds1307;
}

  // Change bits in a control register
  void tm1650_set_control_reg(uint8_t index, uint8_t set, uint8_t mask = 0xFF) {
    if(index > 3)
      return;
    Wire.beginTransmission(TM1650_DCTRL_BASE + index);
    ctrl_save[index] &= mask;
    ctrl_save[index] |= set;
    Wire.write(ctrl_save[index]);
    Wire.endTransmission();
   }


  // Change bits in the digit segment register
  void tm1650_set_digit_reg(uint8_t index, uint8_t segments) {
      if(index > 3)
        return;
      Wire.beginTransmission(TM1650_DIGIT_BASE + index);
      digit_save[index] = segments;
      Wire.write(digit_save[index]);
      Wire.endTransmission();
   }

  // Display a number at an index
  void tm1650_send_digit(uint8_t index, char number){
    char ws[40];
    static uint8_t segment_map[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7f, 0x6f};
    if(number < '0' || number > '9')
      return;

    uint8_t value = ((uint8_t) number) & 0x0F;
    uint8_t segments = segment_map[value];
   
    if(index > 1) // Add colon segment if digit 2 or 3
      segments |= 0x80;
    tm1650_set_digit_reg(index, segments);
  }
 
  // This gets called once from the core before update is called
  void setup() override {
    // Set initialization state
    state = STATE_INIT;
    hw_clock_set = false;

    // Declare a service "alarm_clock_set"
    //  - Service will be called "esphome.<NODE_NAME>_alarm_clock_set" in Home Assistant.
    //  - The service has 2 arguments (type inferred from method definition):
    //     - alarm_hour: integer
    //     - alarm_minute: boolean
    //  - The function set_alarm_time declared below will attached to the service.
    register_service(&XY_Clock::set_alarm_time, "alarm_time_set",
                     {"alarm_hour", "alarm_minute"});
  }

  // This gets called repeatedly from the core at specific intervals after setup is called
  void update() override {
    // This will be called appx. every 1000 milliseconds. ESP8266 WIFI code may delay calls to this so timing will not be reliable.
  
    uint8_t init_cr = TM1650_ENABLE_BIT_ONOFF | (3 << TM1650_BRIGHT_SHIFT); // Enable display with intial brightness of 3

    switch(state){
      case STATE_INIT: // State at power on
        state = STATE_HW_INIT;
        break;

      case STATE_HW_INIT: // State during hardware initialization
       // Initialize register saves
        for( uint8_t i = 0; i < 4; i++){
            ctrl_save[i] = 0;
            digit_save[i] = 0;
        }
        // Set digit string length to zero
        digits[0] = 0;

        // Initialize control registers
        tm1650_set_control_reg(0, init_cr);
        tm1650_set_control_reg(1, init_cr);
        tm1650_set_control_reg(2, init_cr);
        tm1650_set_control_reg(3, init_cr);
         
        // Initially set digits to: --:--
        tm1650_set_digit_reg(0, 0x40);
        tm1650_set_digit_reg(1, 0x40);
        tm1650_set_digit_reg(2, 0xC0);
        tm1650_set_digit_reg(3, 0xC0);
        ESP_LOGD("custom", "Display HW initailized");
        state = STATE_DISPLAY_TIME;
        break;


      case STATE_DISPLAY_TIME:
        {
          char time_digits[7];
          uint8_t hour = 25, minute = 60;

          if(sntp_time->now().is_valid() || homeassistant_time->now().is_valid()){ // See if time is valid
            hour = sntp_time->now().hour;
            minute = sntp_time->now().minute;
            if(!hw_clock_set){
              ESP_LOGD(TAG, "Time valid. Writing time to DS1307");
              ds1307_time->write_time();
              hw_clock_set = true;
            }

          }
          else
            ESP_LOGD(TAG, "No time source is valid");
            
          if((hour < 25) &&(minute < 60)){
            snprintf(time_digits, 7, "%02d%02d", hour, minute);
            for(int i = 0; i < 4; i++)
              tm1650_send_digit(i, time_digits[i]);
          }
        }
        break;


      default:
        state = STATE_HW_INIT;
        break;


    }
 
  }

  /*
  * Set the alarm clock trigger time
  */

  void set_alarm_time(int hour, int minute){


  }
};

Looks to me like your class definition needs to inherit CustomAPIDevice too.

class XY_Clock : public PollingComponent, public CustomAPIDevice {

That was it. Thanks!

1 Like