Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
August 12, 2022 05:21 pm GMT

Build your Smart device for Google Home

Creating Google Home project

We will use Cloud-to-cloud integration method. So we need to navigate to Actions on Google console and create a new project:
Image description
Image description
Image description
And we need to name our Smart Home Action:
Image description
Image description

Set up OAuth2 and backend server

Cloud-to-cloud integration required OAuth2 server for linking your smart device service to Google Home application. In this article, we will implement it with Laravel framework and Passport package. You should pass base steps, like Laravel and Passport installation, database setup, and make fake users or you can find example code here. All additional actions will be described next.

Account linking

Move to your Laravel project and run the command to generate OAuth Client Information for Google Home.

$ php artisan passport:clientWhich user ID should the client be assigned to?:> 1What should we name the client?:> GoogleWhere should we redirect the request after authorization?:> https://oauth-redirect.googleusercontent.com/r/{your project id}New client created successfully.Client ID: 9700039b-92b7-4a79-a421-152747b9a257Client secret: 813PEwdTAq7kf7vRXuyd75dJEaSzAIZ1GDWjIyRM

Pass received data and oauth2 endpoints to Account Linking settings in your project:
Image description

Implement backend

To notify Google Home about our device status, we need to return their data when Google Home requested data by Fulfillment URL. Google Home sends data in 3 types: SYNC, QUERY, and EXECUTE.

Response for sync request will return a list of all devices and their capabilities:

# Request example{    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",    "inputs": [{      "intent": "action.devices.SYNC"    }]}
# Response example{    "requestId": "6894439706274654512",    "payload": {        "agentUserId": "user123",        "devices": [            {                "id": 1,                "type": "action.devices.types.THERMOSTAT",                "traits": [                    "action.devices.traits.TemperatureSetting"                ],                "name": {                    "name": "Thermostat"                },                "willReportState": true,                "attributes": {                    "availableThermostatModes": [                        "off",                        "heat",                        "cool"                    ],                    "thermostatTemperatureRange": {                        "minThresholdCelsius": 18,                        "maxThresholdCelsius": 30                    },                    "thermostatTemperatureUnit": "C"                },                "deviceInfo": {                    "manufacturer": "smart-home-inc"                }            }        ]    }}

Resposne for query request must include a full set of states for each of the traits supported by the requested devices:

# Request example{    "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",    "inputs": [{        "intent": "action.devices.QUERY",        "payload": {            "devices": [{                "id": "1"            }]        }    }]}
# Response example{    "requestId": "6894439706274654514",    "payload": {        "agentUserId": "user123",        "devices": {            "1": {                "status": "SUCCESS",                "online": true,                "thermostatMode": "cool",                "thermostatTemperatureSetpoint": 23,                "thermostatTemperatureAmbient": 10,                "thermostatHumidityAmbient": 10            }        }    }}

Execute request contain data as same as Query but can contain commands given to a group of devices(response is same as Query):

# request example{    "inputs": [        {            "context": {                "locale_country": "US",                "locale_language": "en"            },            "intent": "action.devices.EXECUTE",            "payload": {                "commands": [                    {                        "devices": [                            {                                "id": "1"                            }                        ],                        "execution": [                            {                                "command": "action.devices.commands.ThermostatTemperatureSetpoint",                                "params": {                                    "thermostatTemperatureSetpoint": 25.5                                }                            }                        ]                    }                ]            }        }    ],    "requestId": "15039538743185198388"}

Understanding device object

A device object on Sync request must contain information about a device like its name, device info, including traits, and attributes based on included trains:

# Device object on sync request{    "id": 1,    "type": "action.devices.types.THERMOSTAT",    "traits": [        "action.devices.traits.TemperatureSetting"    ],    "name": {        "name": "Thermostat"    },    "willReportState": true,    "attributes": {        "availableThermostatModes": [            "off",            "heat",            "cool"        ],        "thermostatTemperatureRange": {            "minThresholdCelsius": 18,            "maxThresholdCelsius": 30        },        "thermostatTemperatureUnit": "C"    },    "deviceInfo": {        "manufacturer": "smart-home-inc"    }}

And must contain a device state on Quest or Execute:

# Device object on Query or Execute request{    "status": "SUCCESS",    "online": true,    "thermostatMode": "cool",    "thermostatTemperatureSetpoint": 24,    "thermostatTemperatureAmbient": 10,    "thermostatHumidityAmbient": 10}

Return and save device state

Move to your Laravel project and create a Thermostat model:

php artisan make:model Thermostat -m
# database/migrations/2022_08_11_154357_create_thermostats_table.php<?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;return new class extends Migration{    public function up()    {        Schema::create('thermostats', function (Blueprint $table) {            $table->id();            $table->boolean('online')->default(false);            $table->string('mode');            $table->unsignedInteger('current_temperature')->default(0);            $table->unsignedInteger('expected_temperature')->default(15);            $table->unsignedInteger('humidity')->default(0);            $table->timestamps();        });    }    public function down()    {        Schema::dropIfExists('thermostats');    }};
# app/Models/Thermostate.php<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;class Thermostat extends Model{    protected $fillable = [        'online',        'mode',        'current_temperature',        'expected_temperature',        'humidity',    ];    protected $casts = [        'online' => 'boolean',    ];}
php artisan migrate

Implement Fulfillment URL route

# routes/api.php<?phpuse App\Http\Controllers\FulfillmentController;use Illuminate\Support\Facades\Route;Route::post('/', FulfillmentController::class);
<?phpnamespace App\Http\Controllers;use App\Models\Thermostat;use Illuminate\Http\Request;use Illuminate\Support\Arr;class FulfillmentController extends Controller{    public function __invoke(Request $request)    {        $response = null;        // Extract request type        switch ($request->input('inputs.0.intent')) {            case 'action.devices.QUERY':                $response = $this->queryResponse();                break;            case 'action.devices.SYNC':                $response = $this->syncRequest();                break;            case 'action.devices.EXECUTE':                $response = $this->syncExecute($this->syncExecute($request->input('inputs.0.payload.commands'))); // Extract list of commands                break;        }        return $response;    }    private function queryResponse()    {        $devices = [];        // Extract our devices states        foreach (Thermostat::all() as $thermostat) {            $devices[$thermostat->id] = [                'status' => 'SUCCESS',                'online' => $thermostat->online,                'thermostatMode' => $thermostat->mode,                'thermostatTemperatureSetpoint' => $thermostat->expected_temperature,                'thermostatTemperatureAmbient' => $thermostat->current_temperature,                'thermostatHumidityAmbient' => $thermostat->humidity,            ];        }        return response([            'requestId' => "6894439706274654514",            'payload' => [                "agentUserId" => "user123",                'devices' => $devices,            ],        ]);    }    private function syncRequest()    {        $devices = [];        // Define our devices        foreach (Thermostat::all() as $thermostat) {            $devices[] = [                'id' => $thermostat->id,                'type' => "action.devices.types.THERMOSTAT",                'traits' => [                    "action.devices.traits.TemperatureSetting"                ],                'name' => [                    'name' => 'Thermostat'                ],                'willReportState' => true,                'attributes' => [                    'availableThermostatModes' => [                        'off',                        'heat',                        'cool',                    ],                    'thermostatTemperatureRange' => [                        'minThresholdCelsius' => 18,                        'maxThresholdCelsius' => 30,                    ],                    'thermostatTemperatureUnit' => 'C'                ],                'deviceInfo' => [                    'manufacturer' => 'smart-home-inc',                ],            ];        }        return response([            'requestId' => "6894439706274654512",            'payload' => [                "agentUserId" => "user123",                'devices' => $devices,            ],        ]);    }    private function syncExecute(array $commands)    {        foreach ($commands as $command) {            // Get devices for execute command            $thermostats = Thermostat::whereIn('id', Arr::pluck($command['devices'], 'id'))->get();            foreach ($command['execution'] as $executionItem) {                switch ($executionItem['command']) {                    // Handle set point command and save it in our model                    case 'action.devices.commands.ThermostatTemperatureSetpoint':                        foreach ($thermostats as $thermostat) {                            $thermostat->update([                                'expected_temperature' => $executionItem['params']['thermostatTemperatureSetpoint'],                            ]);                        }                        break;                    // Handle set set mode command and save it in our model                    case 'action.devices.commands.ThermostatSetMode':                        foreach ($thermostats as $thermostat) {                            $thermostat->update([                                'mode' => $executionItem['params']['thermostatMode'],                            ]);                        }                        break;                }            }        }        // It not necessary to return data for command request        return response([]);    }}

Return to Console actions and set your Fulfillment URL in your project settings:
Image description
And create your device in your database:
Image description

Simplify the authentication process:

Since we are not interested in authentication details we can skip the implement login page and force the authenticated user:

# app/Providers/AppServiceProvider.php<?phpnamespace App\Providers;use App\Models\User;use Illuminate\Support\Facades\Auth;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{    public function register()    {        //    }    public function boot()    {        Auth::setUser(User::first());    }}

Link backend with Google Home

After previous actions, you can test your backend with Google Home app which is authenticated as the same account used for creating Actions Console project.
Image description
After linking your device will appear in Google Home:
Image description
Now you can control your "device" and its state will be saved on the database. You can trigger sync(reconnect account), query(swipe update gesture), and execute(try to change mode).

You can find more about available device types and traits Here


Original Link: https://dev.to/davidnadejdin/build-your-for-google-home-2b2a

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To