Pedro Casagrande
Pedro Casagrande

Reputation: 167

SmartHomeApp: reportState on Java always returning INVALID_ARGUMENT

Whenever I get a update from some device on my SmartHomeApp I run a reportState call, but I'm always an io.grpc.StatusRuntimeException with "INVALID_ARGUMENT: Request contains an invalid argument." message.

I've followed the instruction on https://developers.google.com/actions/smarthome/develop/report-state and the Java implementation. The only difference is that I'dont use the same requestId as the execute call as usually the reportState method is called after an MQTT update from the physical device itself.

public void reportState(Device device) {
   User user = device.getHub().getUser();
   String requestId = UUID.randomUUID().toString();
   String agentId = user.getId().toString();
   Struct.Builder stateBuilder = Struct.newBuilder();
   if (device.getType().getTraits().contains(GoogleDeviceTrait.ON_OFF)) {
      boolean state = "on".equalsIgnoreCase(device.getState());
      stateBuilder.putFields("on", Value.newBuilder().setBoolValue(state).build());
   }
   if (device.getType().getTraits().contains(GoogleDeviceTrait.OPEN_CLOSE)) {
      int openPercent = device.getState() != null ? Integer.valueOf(device.getState()) : 0;
      stateBuilder.putFields("openPercent", Value.newBuilder().setNumberValue(openPercent).build());
   }
   try {
      smartHomeApp.reportState(ReportStateAndNotificationRequest.newBuilder()
            .setRequestId(requestId)
            .setAgentUserId(agentId)
            .setPayload(StateAndNotificationPayload.newBuilder()
                  .setDevices(ReportStateAndNotificationDevice.newBuilder()
                        .setStates(stateBuilder.build())
                        .build()
                  )
                  .build()
            )
            .build()
      );
   } catch (Exception ex) {
      ex.printStackTrace();
   }
}

I'm guessing the problem is that I'm not passing the device name or any device identifier, but it doesn't seems to have an method on the builders for this.

Upvotes: 2

Views: 266

Answers (1)

Nick Felker
Nick Felker

Reputation: 11978

The Java library uses protobuf Struct objects to create the state object. The documentation does actually seem to be incorrect in this regard, as if you were to compare the Java code that your snippet creates:

{
  requestId: '123ABC',
  agentUserId: 'user-123',
  payload: {
    devices: {
      states: {
        on: true,
        openPercent: 50
      }
    }
  }
}

While we do provide states, there is no device ID, so it's unclear what device this state belongs to. As such, this will result in an invalid argument.

You'll need to wrap your state object in another struct that contains the device identifier.

Struct.Builder deviceStateBuilder = Struct.newBuilder()
  .putFields("device1", stateBuilder.build()
  .build()

smartHomeApp.reportState(ReportStateAndNotificationRequest.newBuilder()
  .setRequestId(requestId)
  .setAgentUserId(agentId)
  .setPayload(StateAndNotificationPayload.newBuilder()
    .setDevices(ReportStateAndNotificationDevice.newBuilder()
      .setStates(deviceStateBuilder.build())
      .build()
    )
    .build()
  )
.build()

With the initial release of smart home support in the Java/Kotlin library, we deferred a lot to the underlying protobuf objects to reduce the number of APIs being created and reviewed. As we go forward, it may be a good idea to take a look at where we can make improvements to the developer experience. If you have feedback, I invite you to visit the library's GitHub page and file an issue.

Upvotes: 1

Related Questions