Google Home용 «스마트 장치» 구축

44696 단어 phplaravelgooglehome

Google 홈 프로젝트 생성



Cloud-to-cloud 통합 방법을 사용합니다. 따라서 Actions on Google 콘솔로 이동하여 새 프로젝트를 생성해야 합니다.



그리고 Smart Home Action의 이름을 지정해야 합니다.


OAuth2 및 백엔드 서버 설정



Cloud-to-cloud 통합이 필요합니다OAuth2 스마트 장치 서비스를 Google Home 애플리케이션에 연결하기 위한 서버. 이 기사에서는 Laravel frameworkPassport package 으로 구현합니다. LaravelPassport 설치, 데이터베이스 설정과 같은 기본 단계를 통과하고 가짜 사용자를 만들거나 예제 코드here를 찾을 수 있습니다. 모든 추가 작업은 다음에 설명합니다.

계정 연결



Laravel 프로젝트로 이동하고 명령을 실행하여 Google Home용 OAuth 클라이언트 정보를 생성합니다.

$ php artisan passport:client

Which user ID should the client be assigned to?:
> 1

What should we name the client?:
> Google

Where 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-152747b9a257
Client secret: 813PEwdTAq7kf7vRXuyd75dJEaSzAIZ1GDWjIyRM


수신된 데이터 및 oauth2 끝점을 프로젝트의 계정 연결 설정에 전달합니다.

백엔드 구현



기기 상태에 대해 Google Home에 알리려면 Google Home이 Fulfillment URL로 데이터를 요청했을 때 데이터를 반환해야 합니다. Google Home은 SYNC, QUERY 및 EXECUTE의 세 가지 유형으로 데이터를 전송합니다.

동기화 요청에 대한 응답은 모든 장치 및 해당 기능 목록을 반환합니다.

# 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"
                }
            }
        ]
    }
}


쿼리 요청에 대한 응답에는 요청된 장치에서 지원하는 각 특성에 대한 전체 상태 세트가 포함되어야 합니다.

# 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
            }
        }
    }
}


실행 요청은 Query와 동일한 데이터를 포함하지만 장치 그룹에 주어진 명령을 포함할 수 있습니다(응답은 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"
}


장치 개체 이해



동기화 요청의 장치 개체는 이름, 장치 정보(특성 포함), 포함된 열차를 기반으로 하는 속성과 같은 장치에 대한 정보를 포함해야 합니다.

# 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"
    }
}


그리고 Quest 또는 Execute의 기기 상태를 포함해야 합니다.

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


장치 상태 반환 및 저장



Laravel 프로젝트로 이동하여 온도 조절기 모델을 만듭니다.

php artisan make:model Thermostat -m



# database/migrations/2022_08_11_154357_create_thermostats_table.php

<?php

use 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
<?php

namespace 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


처리 URL 경로 구현




# routes/api.php

<?php

use App\Http\Controllers\FulfillmentController;
use Illuminate\Support\Facades\Route;

Route::post('/', FulfillmentController::class);



<?php

namespace 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([]);
    }
}


콘솔 작업으로 돌아가서 프로젝트 설정에서 이행 URL을 설정합니다.

그리고 데이터베이스에 장치를 생성합니다.

인증 프로세스 간소화:



우리는 인증 세부 사항에 관심이 없기 때문에 구현 로그인 페이지를 건너뛰고 인증된 사용자를 강제할 수 있습니다.

# app/Providers/AppServiceProvider.php

<?php

namespace 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());
    }
}


Google Home과 백엔드 연결



이전 작업 후에는 Actions Console 프로젝트를 만드는 데 사용된 것과 동일한 계정으로 인증된 Google Home 앱으로 백엔드를 테스트할 수 있습니다.

연결하면 기기가 Google Home에 표시됩니다.

이제 "장치"를 제어할 수 있으며 해당 상태가 데이터베이스에 저장됩니다. 동기화(계정 다시 연결), 쿼리(스와이프 업데이트 제스처) 및 실행(모드 변경 시도)을 트리거할 수 있습니다.

사용 가능한 장치 유형 및 특성에 대해 자세히 알아볼 수 있습니다Here.

좋은 웹페이지 즐겨찾기