Search code examples
c#jsonactions-on-googlegoogle-smart-home

How to respond to a QUERY intent for devices with a genuinely unknown state (willReportState = false, commandOnly... = true insufficient)?


Problem

The documentation doesn't appear to cover how to respond to a QUERY request when you genuinely do not know the state of a given device. Even though I say that willReportState is false for every device and include various commandOnly attributes in the SYNC response, I am nevertheless sent a QUERY request. The same issue also applies to using ReportState calls triggered by a SYNC or QUERY request.

The state is unknown because it's proprietary lighting. I can send it commands, and cache the values of those commands, but at the outset I have no means of telling the existing state. I'm very much aware that I could cache all historic values I see in a database and retrieve them, but that doesn't stop the hardware being controlled from elsewhere without me knowing, so I never truly know the state except at the moment I'm executing an EXECUTE command (which I then report accordingly).

This is very similar to this question (Google Home - Is reporting state mandatory?), but in my case I genuinely don't/can't know the state, so any implementation I give providing a state is a guess/hack.

Old Query Response

{
    "requestId": "SomeMatchingRequestId",
    "payload": {
        "devices": [{
            "id": "SomeValidDeviceId",
            "online": true,
            "status": "SUCCESS"
        }]
    }
 }

Slightly Improved But Suboptimal Response

The following seems to satisfy the request. The issue with this is not only that these values might be wrong in real life, after a few commands they might contradict what's in the UI, too.

{
    "requestId": "SomeMatchingRequestId",
    "payload": {
        "devices": [{
            "id": "SomeValidDeviceId",
            "online": true,
            "on": 0, /* Adding a default value */
            "brightness": 0, /* Adding a default value */
            "color": { "spectrumRGB": 0 }, /* Adding a default value */
            "status": "SUCCESS"
        }]
    }
}

The Device As Reported

Note the attributes, one of which is undocumented, but I added it based on the pattern of the naming.

var device = new SyncResponseDevice
{
    Id = deviceName,
    Type = Types.Light.ToString(),
    Traits = new List<string>
    {
        Traits.Brightness,
        Traits.ColorSetting,
        Traits.OnOff,
    },
    Name = new SyncResponseDeviceName { Name = zoneName },
    WillReportState = false,
    Attributes = new Dictionary<string, object>
    {
        {"commandOnlyBrightness", true},
        {"commandOnlyOnOff", true},
        {"commandOnlyColorSetting", true},
        {"colorModel", colorModel.ToString().ToLower()}
    }
};

Solution

  • We've now been published / certified. Unfortunately this process contradicted some of what Nick's suggested. I suspect Nick's answer is the best one for an implementation, but in order to be certified here's what we had to do...

    1. Every single device MUST be marked as willReportState=true in the Test Suite tests, even if you can't report a genuine state, otherwise it will be rejected.

    2. ReportState MUST be implemented in relation to responding specifically to EXECUTE requests. In our case we could implement this, in that you trigger an asynchronous ReportState call with the exact same state provided in the command. In real life it's possible the state may have changed regardless, but doing the above satisfies the tests.

    3. From what I can tell it's not necessary to implement ReportState in response to QUERY and SYNC calls in order to be certified, although it's probably advisable if you can. In our case we're forced to respond with a fake/default state in this case.

    4. You can responding to QUERY and SYNC responses with either the error response Nick suggested or a fake/default state. Either "work" and have no visible side effects to end users, but probably best to take Nick's approach.

    5. Mentioning Nick suggested an exception should be granted will be ignored. :(

    6. Good luck passing the tests in the Test Suite even with a flawless implementation. It relies on an artificial voice coming out of your speakers being correctly interpreted by a Google Home device. It might be because my device is UK English vs US. I had around a 90% success rate for this, but where I had around 20 tests to run this meant it failed the batch over and over. It took more than an hour of repeating the tests for it to pass. I was using studio grade monitor speakers at a loud volume adjacent to the Google Home device, and it didn't seem to help. I had things like "Set {device} brightness to 75%" be picked up as "You want me to set the device, is that correct?". Good grief. Why this test involves any audio whatsoever confuses me. Why not just message the command handler directly? Anyway, good luck!

    It's my hope that having raised this as an issue the Google Home team will take a look at the above situation, and in turn the certification process updated, too. At that point this answer will hopefully become redundant.