Offbeat-IoT · Small devices. Big APIs.
Integrations
Spotify Integration Google Health Integration Todoist Integration Google Calendar Integration Alexa Integration Google Home Integration
Built-in
Time Integration Ponder Integration Encouragement Integration API OTA updates
Using the Google Home integration

Using the Google Home integration

These sections show the websocket JSON sent to the device for the supported Google Home execute intents together with matching Arduino device examples from microcontroller-examples.

turn on
Websocket command (JSON)
{
  "endpointId" : "123",
  "powerstate" : "TurnOn"
}
Websocket command (CBOR)
{"endpointId": "123", "powerstate": "TurnOn"}
Arduino device example
void applyGoogleHomePowerState(const char* endpointId, const char* powerState) {
  Serial.print("Apply Google Home power command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(powerState);

  if (strcmp(powerState, "TurnOn") == 0) {
    Serial.println("Turn your output on here");
  } else if (strcmp(powerState, "TurnOff") == 0) {
    Serial.println("Turn your output off here");
  } else {
    Serial.println("Unsupported Google Home power command");
    return;
  }

  Serial.print("POWERSTATE=");
  Serial.println(powerState);
}

void handleGoogleHomePowerCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home power command");
    return;
  }

  JsonVariantConst powerStateValue = root[offbeat::google_home::examples::kPowerStateKey];
  if (powerStateValue.isNull()) {
    Serial.println("No Google Home powerstate payload");
    return;
  }

  applyGoogleHomePowerState(
      offbeat::google_home::examples::readEndpointId(root),
      offbeat::google_home::examples::readString(powerStateValue));
  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
turn off
Websocket command (JSON)
{
  "endpointId" : "123",
  "powerstate" : "TurnOff"
}
Websocket command (CBOR)
{"endpointId": "123", "powerstate": "TurnOff"}
Arduino device example
void applyGoogleHomePowerState(const char* endpointId, const char* powerState) {
  Serial.print("Apply Google Home power command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(powerState);

  if (strcmp(powerState, "TurnOn") == 0) {
    Serial.println("Turn your output on here");
  } else if (strcmp(powerState, "TurnOff") == 0) {
    Serial.println("Turn your output off here");
  } else {
    Serial.println("Unsupported Google Home power command");
    return;
  }

  Serial.print("POWERSTATE=");
  Serial.println(powerState);
}

void handleGoogleHomePowerCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home power command");
    return;
  }

  JsonVariantConst powerStateValue = root[offbeat::google_home::examples::kPowerStateKey];
  if (powerStateValue.isNull()) {
    Serial.println("No Google Home powerstate payload");
    return;
  }

  applyGoogleHomePowerState(
      offbeat::google_home::examples::readEndpointId(root),
      offbeat::google_home::examples::readString(powerStateValue));
  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
adjust volume
Websocket command (JSON)
{
  "endpointId" : "123",
  "volumesteps" : -1
}
Websocket command (CBOR)
{"endpointId": "123", "volumesteps": -1}
Arduino device example
void applyGoogleHomeSetVolume(const char* endpointId, long volume) {
  Serial.print("Set volume on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(volume);

  Serial.print("VOLUME=");
  Serial.println(volume);
}

void applyGoogleHomeAdjustVolume(const char* endpointId, long volumeSteps) {
  Serial.print("Adjust volume on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(volumeSteps);

  Serial.print("VOLUME_STEPS=");
  Serial.println(volumeSteps);
}

void handleGoogleHomeVolumeCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home volume command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst volumeValue = root[offbeat::google_home::examples::kVolumeKey];
  JsonVariantConst volumeStepsValue = root[offbeat::google_home::examples::kVolumeStepsKey];

  if (!volumeValue.isNull()) {
    applyGoogleHomeSetVolume(endpointId, offbeat::google_home::examples::readLong(volumeValue));
  } else if (!volumeStepsValue.isNull()) {
    applyGoogleHomeAdjustVolume(endpointId, offbeat::google_home::examples::readLong(volumeStepsValue));
  } else {
    Serial.println("No Google Home volume payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set volume
Websocket command (JSON)
{
  "volume" : 665,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"volume": 665, "endpointId": "123"}
Arduino device example
void applyGoogleHomeSetVolume(const char* endpointId, long volume) {
  Serial.print("Set volume on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(volume);

  Serial.print("VOLUME=");
  Serial.println(volume);
}

void applyGoogleHomeAdjustVolume(const char* endpointId, long volumeSteps) {
  Serial.print("Adjust volume on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(volumeSteps);

  Serial.print("VOLUME_STEPS=");
  Serial.println(volumeSteps);
}

void handleGoogleHomeVolumeCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home volume command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst volumeValue = root[offbeat::google_home::examples::kVolumeKey];
  JsonVariantConst volumeStepsValue = root[offbeat::google_home::examples::kVolumeStepsKey];

  if (!volumeValue.isNull()) {
    applyGoogleHomeSetVolume(endpointId, offbeat::google_home::examples::readLong(volumeValue));
  } else if (!volumeStepsValue.isNull()) {
    applyGoogleHomeAdjustVolume(endpointId, offbeat::google_home::examples::readLong(volumeStepsValue));
  } else {
    Serial.println("No Google Home volume payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set color
Websocket command (JSON)
{
  "saturation" : 123.0,
  "brightness" : 456.0,
  "endpointId" : "123",
  "hue" : 665.0
}
Websocket command (CBOR)
{"saturation": 123.0, "brightness": 456.0, "endpointId": "123", "hue": 665.0}
Arduino device example
void applyGoogleHomeColorCommand(const char* endpointId, double hue, double saturation, double brightness) {
  Serial.print("Set color on ");
  Serial.println(endpointId);
  offbeat::google_home::examples::printLine("Hue: ", hue, 2);
  offbeat::google_home::examples::printLine("Saturation: ", saturation, 2);
  offbeat::google_home::examples::printLine("Brightness: ", brightness, 2);

  Serial.print("HUE=");
  Serial.println(hue, 2);
  Serial.print("SATURATION=");
  Serial.println(saturation, 2);
  Serial.print("BRIGHTNESS=");
  Serial.println(brightness, 2);
}

void handleGoogleHomeColorCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home color command");
    return;
  }

  JsonVariantConst hueValue = root[offbeat::google_home::examples::kHueKey];
  JsonVariantConst saturationValue = root[offbeat::google_home::examples::kSaturationKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];
  if (hueValue.isNull() || saturationValue.isNull() || brightnessValue.isNull()) {
    Serial.println("Incomplete Google Home color payload");
    return;
  }

  applyGoogleHomeColorCommand(
      offbeat::google_home::examples::readEndpointId(root),
      offbeat::google_home::examples::readDouble(hueValue),
      offbeat::google_home::examples::readDouble(saturationValue),
      offbeat::google_home::examples::readDouble(brightnessValue));
  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set color temperature
Websocket command (JSON)
{
  "endpointId" : "123",
  "setColorTemp" : 4000
}
Websocket command (CBOR)
{"endpointId": "123", "setColorTemp": 4000}
Arduino device example
void applyGoogleHomeColorCommand(const char* endpointId, double hue, double saturation, double brightness) {
  Serial.print("Set color on ");
  Serial.println(endpointId);
  offbeat::google_home::examples::printLine("Hue: ", hue, 2);
  offbeat::google_home::examples::printLine("Saturation: ", saturation, 2);
  offbeat::google_home::examples::printLine("Brightness: ", brightness, 2);

  Serial.print("HUE=");
  Serial.println(hue, 2);
  Serial.print("SATURATION=");
  Serial.println(saturation, 2);
  Serial.print("BRIGHTNESS=");
  Serial.println(brightness, 2);
}

void handleGoogleHomeColorCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home color command");
    return;
  }

  JsonVariantConst hueValue = root[offbeat::google_home::examples::kHueKey];
  JsonVariantConst saturationValue = root[offbeat::google_home::examples::kSaturationKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];
  if (hueValue.isNull() || saturationValue.isNull() || brightnessValue.isNull()) {
    Serial.println("Incomplete Google Home color payload");
    return;
  }

  applyGoogleHomeColorCommand(
      offbeat::google_home::examples::readEndpointId(root),
      offbeat::google_home::examples::readDouble(hueValue),
      offbeat::google_home::examples::readDouble(saturationValue),
      offbeat::google_home::examples::readDouble(brightnessValue));
  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set brightness to 456
Websocket command (JSON)
{
  "brightness" : 456,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"brightness": 456, "endpointId": "123"}
Arduino device example
void applyGoogleHomeBrightnessPercent(const char* endpointId, long brightnessPercent) {
  Serial.print("Set brightness percent on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessPercent);

  Serial.print("BRIGHTNESS_PERCENT=");
  Serial.println(brightnessPercent);
}

void applyGoogleHomeBrightnessWeight(const char* endpointId, long brightnessWeight) {
  Serial.print("Adjust brightness weight on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(brightnessWeight);

  Serial.print("BRIGHTNESS_WEIGHT=");
  Serial.println(brightnessWeight);
}

void applyGoogleHomeBrightnessAbsolute(const char* endpointId, long brightnessValue) {
  Serial.print("Set absolute brightness on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessValue);

  Serial.print("BRIGHTNESS=");
  Serial.println(brightnessValue);
}

void handleGoogleHomeBrightnessCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home brightness command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst percentValue = root[offbeat::google_home::examples::kAdjustBrightnessPercentKey];
  JsonVariantConst weightValue = root[offbeat::google_home::examples::kAdjustBrightnessWeightKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];

  if (!percentValue.isNull()) {
    applyGoogleHomeBrightnessPercent(endpointId, offbeat::google_home::examples::readLong(percentValue));
  } else if (!weightValue.isNull()) {
    applyGoogleHomeBrightnessWeight(endpointId, offbeat::google_home::examples::readLong(weightValue));
  } else if (!brightnessValue.isNull()) {
    applyGoogleHomeBrightnessAbsolute(endpointId, offbeat::google_home::examples::readLong(brightnessValue));
  } else {
    Serial.println("No Google Home brightness payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set brightness to 416
Websocket command (JSON)
{
  "brightness" : 416,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"brightness": 416, "endpointId": "123"}
Arduino device example
void applyGoogleHomeBrightnessPercent(const char* endpointId, long brightnessPercent) {
  Serial.print("Set brightness percent on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessPercent);

  Serial.print("BRIGHTNESS_PERCENT=");
  Serial.println(brightnessPercent);
}

void applyGoogleHomeBrightnessWeight(const char* endpointId, long brightnessWeight) {
  Serial.print("Adjust brightness weight on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(brightnessWeight);

  Serial.print("BRIGHTNESS_WEIGHT=");
  Serial.println(brightnessWeight);
}

void applyGoogleHomeBrightnessAbsolute(const char* endpointId, long brightnessValue) {
  Serial.print("Set absolute brightness on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessValue);

  Serial.print("BRIGHTNESS=");
  Serial.println(brightnessValue);
}

void handleGoogleHomeBrightnessCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home brightness command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst percentValue = root[offbeat::google_home::examples::kAdjustBrightnessPercentKey];
  JsonVariantConst weightValue = root[offbeat::google_home::examples::kAdjustBrightnessWeightKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];

  if (!percentValue.isNull()) {
    applyGoogleHomeBrightnessPercent(endpointId, offbeat::google_home::examples::readLong(percentValue));
  } else if (!weightValue.isNull()) {
    applyGoogleHomeBrightnessWeight(endpointId, offbeat::google_home::examples::readLong(weightValue));
  } else if (!brightnessValue.isNull()) {
    applyGoogleHomeBrightnessAbsolute(endpointId, offbeat::google_home::examples::readLong(brightnessValue));
  } else {
    Serial.println("No Google Home brightness payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
adjust brightness percent
Websocket command (JSON)
{
  "adjustBrightnessPercent" : 20,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"adjustBrightnessPercent": 20, "endpointId": "123"}
Arduino device example
void applyGoogleHomeBrightnessPercent(const char* endpointId, long brightnessPercent) {
  Serial.print("Set brightness percent on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessPercent);

  Serial.print("BRIGHTNESS_PERCENT=");
  Serial.println(brightnessPercent);
}

void applyGoogleHomeBrightnessWeight(const char* endpointId, long brightnessWeight) {
  Serial.print("Adjust brightness weight on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(brightnessWeight);

  Serial.print("BRIGHTNESS_WEIGHT=");
  Serial.println(brightnessWeight);
}

void applyGoogleHomeBrightnessAbsolute(const char* endpointId, long brightnessValue) {
  Serial.print("Set absolute brightness on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessValue);

  Serial.print("BRIGHTNESS=");
  Serial.println(brightnessValue);
}

void handleGoogleHomeBrightnessCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home brightness command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst percentValue = root[offbeat::google_home::examples::kAdjustBrightnessPercentKey];
  JsonVariantConst weightValue = root[offbeat::google_home::examples::kAdjustBrightnessWeightKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];

  if (!percentValue.isNull()) {
    applyGoogleHomeBrightnessPercent(endpointId, offbeat::google_home::examples::readLong(percentValue));
  } else if (!weightValue.isNull()) {
    applyGoogleHomeBrightnessWeight(endpointId, offbeat::google_home::examples::readLong(weightValue));
  } else if (!brightnessValue.isNull()) {
    applyGoogleHomeBrightnessAbsolute(endpointId, offbeat::google_home::examples::readLong(brightnessValue));
  } else {
    Serial.println("No Google Home brightness payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
adjust brightness weight
Websocket command (JSON)
{
  "endpointId" : "123",
  "adjustBrightnessWeight" : -1
}
Websocket command (CBOR)
{"endpointId": "123", "adjustBrightnessWeight": -1}
Arduino device example
void applyGoogleHomeBrightnessPercent(const char* endpointId, long brightnessPercent) {
  Serial.print("Set brightness percent on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessPercent);

  Serial.print("BRIGHTNESS_PERCENT=");
  Serial.println(brightnessPercent);
}

void applyGoogleHomeBrightnessWeight(const char* endpointId, long brightnessWeight) {
  Serial.print("Adjust brightness weight on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(brightnessWeight);

  Serial.print("BRIGHTNESS_WEIGHT=");
  Serial.println(brightnessWeight);
}

void applyGoogleHomeBrightnessAbsolute(const char* endpointId, long brightnessValue) {
  Serial.print("Set absolute brightness on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(brightnessValue);

  Serial.print("BRIGHTNESS=");
  Serial.println(brightnessValue);
}

void handleGoogleHomeBrightnessCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home brightness command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst percentValue = root[offbeat::google_home::examples::kAdjustBrightnessPercentKey];
  JsonVariantConst weightValue = root[offbeat::google_home::examples::kAdjustBrightnessWeightKey];
  JsonVariantConst brightnessValue = root[offbeat::google_home::examples::kBrightnessKey];

  if (!percentValue.isNull()) {
    applyGoogleHomeBrightnessPercent(endpointId, offbeat::google_home::examples::readLong(percentValue));
  } else if (!weightValue.isNull()) {
    applyGoogleHomeBrightnessWeight(endpointId, offbeat::google_home::examples::readLong(weightValue));
  } else if (!brightnessValue.isNull()) {
    applyGoogleHomeBrightnessAbsolute(endpointId, offbeat::google_home::examples::readLong(brightnessValue));
  } else {
    Serial.println("No Google Home brightness payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media resume
Websocket command (JSON)
{
  "playerControl" : "Play",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Play", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media stop
Websocket command (JSON)
{
  "playerControl" : "Stop",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Stop", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media next
Websocket command (JSON)
{
  "playerControl" : "Next",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Next", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media previous
Websocket command (JSON)
{
  "playerControl" : "Prev",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Prev", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media pause
Websocket command (JSON)
{
  "playerControl" : "Pause",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Pause", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media seek relative
Websocket command (JSON)
{
  "endpointId" : "123",
  "Seek" : -10000
}
Websocket command (CBOR)
{"endpointId": "123", "Seek": -10000}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media seek absolute
Websocket command (JSON)
{
  "endpointId" : "123",
  "SeekAbsolute" : 30000
}
Websocket command (CBOR)
{"endpointId": "123", "SeekAbsolute": 30000}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media repeat
Websocket command (JSON)
{
  "playerControl" : "Repeat",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Repeat", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
media shuffle
Websocket command (JSON)
{
  "playerControl" : "Shuffle",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "Shuffle", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
closed captioning on
Websocket command (JSON)
{
  "playerControl" : "CaptionsOn",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "CaptionsOn", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
closed captioning off
Websocket command (JSON)
{
  "playerControl" : "CaptionsOff",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"playerControl": "CaptionsOff", "endpointId": "123"}
Arduino device example
void applyGoogleHomePlayerControl(const char* endpointId, const char* playerControl) {
  Serial.print("Apply media command for ");
  Serial.print(endpointId);
  Serial.print(": ");
  Serial.println(playerControl);

  Serial.print("PLAYER_CONTROL=");
  Serial.println(playerControl);
}

void applyGoogleHomeSeekAbsolute(const char* endpointId, long absolutePositionMs) {
  Serial.print("Seek absolute on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(absolutePositionMs);

  Serial.print("SEEK_ABSOLUTE_MS=");
  Serial.println(absolutePositionMs);
}

void applyGoogleHomeSeekRelative(const char* endpointId, long relativePositionMs) {
  Serial.print("Seek relative on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(relativePositionMs);

  Serial.print("SEEK_RELATIVE_MS=");
  Serial.println(relativePositionMs);
}

void handleGoogleHomeMediaCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home media command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst playerControlValue = root[offbeat::google_home::examples::kPlayerControlKey];
  JsonVariantConst seekAbsoluteValue = root[offbeat::google_home::examples::kSeekAbsoluteKey];
  JsonVariantConst seekRelativeValue = root[offbeat::google_home::examples::kSeekRelativeKey];

  if (!playerControlValue.isNull()) {
    applyGoogleHomePlayerControl(endpointId, offbeat::google_home::examples::readString(playerControlValue));
  } else if (!seekAbsoluteValue.isNull()) {
    applyGoogleHomeSeekAbsolute(endpointId, offbeat::google_home::examples::readLong(seekAbsoluteValue));
  } else if (!seekRelativeValue.isNull()) {
    applyGoogleHomeSeekRelative(endpointId, offbeat::google_home::examples::readLong(seekRelativeValue));
  } else {
    Serial.println("No Google Home media payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
select channel
Websocket command (JSON)
{
  "endpointId" : "123",
  "SetChannel" : 9
}
Websocket command (CBOR)
{"endpointId": "123", "SetChannel": 9}
Arduino device example
void applyGoogleHomeSetChannel(const char* endpointId, long channelNumber) {
  Serial.print("Set channel on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(channelNumber);

  Serial.print("SET_CHANNEL=");
  Serial.println(channelNumber);
}

void applyGoogleHomeSkipChannel(const char* endpointId, long channelOffset) {
  Serial.print("Skip channel on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(channelOffset);

  Serial.print("SKIP_CHANNEL=");
  Serial.println(channelOffset);
}

void handleGoogleHomeChannelCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home channel command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst setChannelValue = root[offbeat::google_home::examples::kSetChannelKey];
  JsonVariantConst skipChannelValue = root[offbeat::google_home::examples::kSkipChannelKey];

  if (!setChannelValue.isNull()) {
    applyGoogleHomeSetChannel(endpointId, offbeat::google_home::examples::readLong(setChannelValue));
  } else if (!skipChannelValue.isNull()) {
    applyGoogleHomeSkipChannel(endpointId, offbeat::google_home::examples::readLong(skipChannelValue));
  } else {
    Serial.println("No Google Home channel payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
relative channel
Websocket command (JSON)
{
  "skipChannel" : 1,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"skipChannel": 1, "endpointId": "123"}
Arduino device example
void applyGoogleHomeSetChannel(const char* endpointId, long channelNumber) {
  Serial.print("Set channel on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(channelNumber);

  Serial.print("SET_CHANNEL=");
  Serial.println(channelNumber);
}

void applyGoogleHomeSkipChannel(const char* endpointId, long channelOffset) {
  Serial.print("Skip channel on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(channelOffset);

  Serial.print("SKIP_CHANNEL=");
  Serial.println(channelOffset);
}

void handleGoogleHomeChannelCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home channel command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst setChannelValue = root[offbeat::google_home::examples::kSetChannelKey];
  JsonVariantConst skipChannelValue = root[offbeat::google_home::examples::kSkipChannelKey];

  if (!setChannelValue.isNull()) {
    applyGoogleHomeSetChannel(endpointId, offbeat::google_home::examples::readLong(setChannelValue));
  } else if (!skipChannelValue.isNull()) {
    applyGoogleHomeSkipChannel(endpointId, offbeat::google_home::examples::readLong(skipChannelValue));
  } else {
    Serial.println("No Google Home channel payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
return channel
Websocket command (JSON)
{
  "skipChannel" : -1,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"skipChannel": -1, "endpointId": "123"}
Arduino device example
void applyGoogleHomeSetChannel(const char* endpointId, long channelNumber) {
  Serial.print("Set channel on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.println(channelNumber);

  Serial.print("SET_CHANNEL=");
  Serial.println(channelNumber);
}

void applyGoogleHomeSkipChannel(const char* endpointId, long channelOffset) {
  Serial.print("Skip channel on ");
  Serial.print(endpointId);
  Serial.print(" by ");
  Serial.println(channelOffset);

  Serial.print("SKIP_CHANNEL=");
  Serial.println(channelOffset);
}

void handleGoogleHomeChannelCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home channel command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst setChannelValue = root[offbeat::google_home::examples::kSetChannelKey];
  JsonVariantConst skipChannelValue = root[offbeat::google_home::examples::kSkipChannelKey];

  if (!setChannelValue.isNull()) {
    applyGoogleHomeSetChannel(endpointId, offbeat::google_home::examples::readLong(setChannelValue));
  } else if (!skipChannelValue.isNull()) {
    applyGoogleHomeSkipChannel(endpointId, offbeat::google_home::examples::readLong(skipChannelValue));
  } else {
    Serial.println("No Google Home channel payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set blind tilt percent
Websocket command (JSON)
{
  "setTiltPercent" : 60,
  "endpointId" : "123"
}
Websocket command (CBOR)
{"setTiltPercent": 60, "endpointId": "123"}
Arduino device example
void applyGoogleHomeBlindTiltPercent(const char* endpointId, long tiltPercent) {
  long clampedTiltPercent = clampPercent(tiltPercent);
  long tiltDegrees = percentToDegrees(clampedTiltPercent);

  Serial.print("Set Google Home blind tilt on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.print(clampedTiltPercent);
  Serial.println("%");

  Serial.print("TILT_PERCENT=");
  Serial.println(clampedTiltPercent);
  Serial.print("TILT_DEGREES=");
  Serial.println(tiltDegrees);
}

void applyGoogleHomeBlindTiltDegrees(const char* endpointId, long tiltDegrees) {
  long clampedTiltDegrees = clampDegrees(tiltDegrees);
  long tiltPercent = degreesToPercent(clampedTiltDegrees);

  Serial.print("Set Google Home blind tilt on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.print(clampedTiltDegrees);
  Serial.println(" degrees");

  Serial.print("TILT_DEGREES=");
  Serial.println(clampedTiltDegrees);
  Serial.print("TILT_PERCENT=");
  Serial.println(tiltPercent);
}

void handleGoogleHomeBlindTiltCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home blind tilt command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst rotationPercent = root[offbeat::google_home::examples::kRotationPercentKey];
  JsonVariantConst rotationDegrees = root[offbeat::google_home::examples::kRotationDegreesKey];

  if (!rotationPercent.isNull()) {
    applyGoogleHomeBlindTiltPercent(
        endpointId, offbeat::google_home::examples::readLong(rotationPercent));
  } else if (!rotationDegrees.isNull()) {
    applyGoogleHomeBlindTiltDegrees(
        endpointId, offbeat::google_home::examples::readLong(rotationDegrees));
  } else {
    Serial.println("No Google Home blind tilt payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
set blind tilt degrees
Websocket command (JSON)
{
  "endpointId" : "123",
  "setTiltDegrees" : 90
}
Websocket command (CBOR)
{"endpointId": "123", "setTiltDegrees": 90}
Arduino device example
void applyGoogleHomeBlindTiltPercent(const char* endpointId, long tiltPercent) {
  long clampedTiltPercent = clampPercent(tiltPercent);
  long tiltDegrees = percentToDegrees(clampedTiltPercent);

  Serial.print("Set Google Home blind tilt on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.print(clampedTiltPercent);
  Serial.println("%");

  Serial.print("TILT_PERCENT=");
  Serial.println(clampedTiltPercent);
  Serial.print("TILT_DEGREES=");
  Serial.println(tiltDegrees);
}

void applyGoogleHomeBlindTiltDegrees(const char* endpointId, long tiltDegrees) {
  long clampedTiltDegrees = clampDegrees(tiltDegrees);
  long tiltPercent = degreesToPercent(clampedTiltDegrees);

  Serial.print("Set Google Home blind tilt on ");
  Serial.print(endpointId);
  Serial.print(" to ");
  Serial.print(clampedTiltDegrees);
  Serial.println(" degrees");

  Serial.print("TILT_DEGREES=");
  Serial.println(clampedTiltDegrees);
  Serial.print("TILT_PERCENT=");
  Serial.println(tiltPercent);
}

void handleGoogleHomeBlindTiltCommand(uint8_t* payload, size_t length) {
  StaticJsonDocument<256> document;
  JsonObjectConst root;
  offbeat::google_home::examples::ParseStatus status =
      offbeat::google_home::examples::parseRoot(payload, length, document, root);

  if (status == offbeat::google_home::examples::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse Google Home blind tilt command");
    return;
  }

  const char* endpointId = offbeat::google_home::examples::readEndpointId(root);
  JsonVariantConst rotationPercent = root[offbeat::google_home::examples::kRotationPercentKey];
  JsonVariantConst rotationDegrees = root[offbeat::google_home::examples::kRotationDegreesKey];

  if (!rotationPercent.isNull()) {
    applyGoogleHomeBlindTiltPercent(
        endpointId, offbeat::google_home::examples::readLong(rotationPercent));
  } else if (!rotationDegrees.isNull()) {
    applyGoogleHomeBlindTiltDegrees(
        endpointId, offbeat::google_home::examples::readLong(rotationDegrees));
  } else {
    Serial.println("No Google Home blind tilt payload");
    return;
  }

  offbeat::google_home::examples::printEndpoint(root);
  Serial.println("TEST:PASS");
}
lock lock
Websocket command (JSON)
{
  "endpointId" : "123",
  "lockstate" : "Lock"
}
Websocket command (CBOR)
{"endpointId": "123", "lockstate": "Lock"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
lock unlock
Websocket command (JSON)
{
  "endpointId" : "123",
  "lockstate" : "Unlock"
}
Websocket command (CBOR)
{"endpointId": "123", "lockstate": "Unlock"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
select input
Websocket command (JSON)
{
  "endpointId" : "123",
  "setInput" : "HDMI 1"
}
Websocket command (CBOR)
{"endpointId": "123", "setInput": "HDMI 1"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
toggle on
Websocket command (JSON)
{
  "CustomMute.powerstate" : "TurnOn",
  "endpointId" : "123"
}
Websocket command (CBOR)
{"CustomMute.powerstate": "TurnOn", "endpointId": "123"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
set mode
Websocket command (JSON)
{
  "endpointId" : "123",
  "CustomMode.setMode" : "ModeA"
}
Websocket command (CBOR)
{"endpointId": "123", "CustomMode.setMode": "ModeA"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
activate scene
Websocket command (JSON)
{
  "endpointId" : "123",
  "scenestate" : "TurnOn"
}
Websocket command (CBOR)
{"endpointId": "123", "scenestate": "TurnOn"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]
deactivate scene
Websocket command (JSON)
{
  "endpointId" : "123",
  "scenestate" : "TurnOff"
}
Websocket command (CBOR)
{"endpointId": "123", "scenestate": "TurnOff"}
Arduino device example
Unresolved directive in ../../../target/generated-doc-sections/partials/_google-home-execute-sections.adoc - include::https://raw.githubusercontent.com/Offbeat-IoT/microcontroller-examples/HEAD/examples/google-home/LockCommand/LockCommand.ino[tags=google-home-lock-command-docs,indent=0]

Live camera view

Google can show a live camera view on a smart display or in the Google Home app through action.devices.traits.CameraStream. As with Alexa, the device does not talk to Google and does not serve a stream URL: Offbeat answers action.devices.commands.GetCameraStream, asks the device to push JPEG frames over its existing WebSocket, and returns the media gateway’s signed H.264 MPEG-TS HLS URL.

Note
One device setup, both assistants. The device contract is assistant-agnostic: the same firmware (or mock) receives the same startVideo/stopVideo commands and pushes the same binary JPEG frames and motion readings to Alexa and Google Home; the platform adapts. Enable the CameraStreamController capability for Google Home on the device (exactly as for Alexa) and the same endpoint streams to either, with no device-side change.

Declaring a camera

The camera capability and settings are shared with Alexa: add the CameraStreamController capability to the device (Device integrations → Google Home → Configure), set the display type to CAMERA, and enable the camera stream + resolution in the device’s Camera settings. Offbeat then advertises action.devices.traits.CameraStream with cameraStreamSupportedProtocols ["hls"], cameraStreamNeedAuthToken false and cameraStreamNeedDrmEncryption false.

Execute: GetCameraStream

When Google asks for the stream, Offbeat activates the stream, tells the device to start streaming ({"startVideo": 5}), preloads the playlist and replies with:

{
  "commands": [
    {
      "ids": ["<endpointId>"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "cameraStreamProtocol": "hls",
        "cameraStreamAccessUrl": "https://camera.offbeat-iot.com/s/<token>/index.m3u8"
      }
    }
  ]
}

cameraStreamNeedAuthToken is false because the URL is self-authorizing (the signed token is the secret), so no cameraStreamAuthToken is returned. No Cast receiver app id is returned either, so Google’s default receiver is used. If the camera is disabled, not provisioned, or HLS is not among the requested protocols, the command returns status: "ERROR" with errorCode: "resourceUnavailable".

Stream requirements

Identical to the Alexa live camera view: H.264 Baseline or Main profile with in-band SPS/PPS in every segment (High profile silently fails), served as MPEG-TS HLS over public HTTPS:443 with a trusted certificate, produced by the media gateway. The playlist must be live (HTTP 200) before the response, so Offbeat preloads it (bounded by alexa.camera.preload-timeout-ms, default 3500 ms). Resolutions 640x480 and 800x600, default 5 fps. See Live camera view on the Alexa page for the relay and debugging notes.