Search code examples
google-weave

Update air temperature continuously on Weave Hvac host device


I have an Hvac host device (see interface below) connected to Weave using Libiota and I'm having some trouble understanding how to update the current ambient air temperature (measured on the device). I can measure it but I don't know how to update the state continuously so my Weave client displays the latest ambient air temperature.

I don't fully understand this implementation note in the documentation:

"Changes to the measured ambient temperature should be reported to the cloud service when the temperature value changes by the minimum level of granularity for the device UI, for example in 0.5 or 1 degree. If the device does not support event-based reporting and utilizes polling, the device should confirm a temperature value change of 0.5 or 1 degree, whichever is the UI's level of granularity, and update the cloud service at most once every 90 seconds."

I'm able to update the current air temperature at device initialization (inside hvac_controller.c) and upon set temperature setting (i.e. when the heating temperature changes -inside hvac_controller_traits.c-). I'm using this to update the ambient air temperature: IOTA_MAP_SET(ambient_air_temperature_state, degrees_celsius, myvalue);

Interface:

// Create the example hvac controller interface.
  g_hvac_controller = GoogHvacControllerDevice_create(
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_TEMPERATURE |
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_HUMIDITY |
      GoogHvacControllerDevice_WITH_DISPLAY_UNITS |
      GoogHvacControllerDevice_WITH_HEAT_SUBSYSTEM |
      GoogHvacControllerDevice_WITH_HEAT_SETTING);

Solution

  • After understanding that the device (and not Weave) should poll and update the air temperature state with IOTA_MAP_SET(), I tackled this with a pthread.

    Here's my main.c

    ...    
    #include <pthread.h>
    
    // Function ran by background thread
    void *update_air_temperature() {
      GoogTempSensor* ambient_air_temperature =
          GoogHvacControllerDevice_get_ambient_air_temperature(g_hvac_controller);
      GoogTempSensor_State* ambient_air_temperature_state =
          GoogTempSensor_get_state(ambient_air_temperature);
    
      while(1) {
        // Update air temperature state every 90 seconds
        // Read air temperature from sensor
        // Code to read sensor and assign value to air_temperature
        float air_temperature;
        ...
    
        // Update air temperature state
        IOTA_MAP_SET(ambient_air_temperature_state, degrees_celsius, air_temperature);
    
        sleep(90);
      }
      return NULL;
    }
    
    static IotaDaemon* create_hvac_controller_daemon_(void) {
      IOTA_LOG_INFO("Inside create_hvac_controller_daemon_");
    
      // Create the example hvac controller interface.
      g_hvac_controller = GoogHvacControllerDevice_create(
          GoogHvacControllerDevice_WITH_AMBIENT_AIR_TEMPERATURE |
          GoogHvacControllerDevice_WITH_AMBIENT_AIR_HUMIDITY |
          GoogHvacControllerDevice_WITH_DISPLAY_UNITS |
          GoogHvacControllerDevice_WITH_HEAT_SUBSYSTEM |
          GoogHvacControllerDevice_WITH_HEAT_SETTING);
    
      IotaDevice* iota_device = iota_device_create_from_interface(
          (IotaInterface*)g_hvac_controller, (IotaModelManifestId){"AHXXX"});
      if (iota_device == NULL) {
        IOTA_LOG_ERROR("Device create from interface failed");
        GoogHvacControllerDevice_destroy(g_hvac_controller);
        return NULL;
      }
    
      g_iota_daemon = host_framework_create_daemon("hvac_controller", iota_device);
    
      // Set default state of traits on the hvac_controller.
      example_hvac_controller_configure(g_hvac_controller, g_iota_daemon,
                                        "Heating");
    
      // Background thread to update air temperature
      pthread_t thread1;
      pthread_create(&thread1, NULL, update_air_temperature, NULL);
    
      return g_iota_daemon;
    }
    
    int main(int argc, char** argv) {
      HostIotaFrameworkConfig config = (HostIotaFrameworkConfig){
          .base =
              (IotaFrameworkConfig){
                  .cli_commands = NULL,
                  .num_commands = 0,
                  .builder = create_hvac_controller_daemon_,
              },
          .argc = argc,
          .argv = argv,
          .user_data = NULL,
          .name = "hvac controller",
      };
    
      return host_framework_main(&config);
    }
    

    Don't forget to add -lpthread to the makefile:

    ...
    EXTRA_LIBS=-lpthread
    
    EXAMPLE_OUT_DIR := $(ARCH_OUT_DIR)/examples/$(EXAMPLE)
    ...
    
    $(EXAMPLE_BIN): $(LIBIOTA_STATIC_LIB) $(PLATFORM_STATIC_LIB) \
            $(EXAMPLES_COMMON_LIB) $(HOST_DEVFW_LIB) \
            $(EXAMPLE_OBJECTS) | $(EXAMPLE_OUT_DIR)
            $(CC) -Wl,--start-group $^ -Wl,--end-group -o $@ $(LDLIBS) $(PLATFORM_LDLIBS) $(EXTRA_LIBS)
    ...
    

    Keep in mind that you should only report the new air temperature when it changes, and at a max frequency of 90 seconds. From the documentation:

    Changes to the measured ambient temperature should be reported to the cloud service when the temperature value changes by the minimum level of granularity for the device UI, for example in 0.5 or 1 degree. If the device does not support event-based reporting and utilizes polling, the device should confirm a temperature value change of 0.5 or 1 degree, whichever is the UI's level of granularity, and update the cloud service at most once every 90 seconds.