스마트 홈 작업 개선 및 보안 강화

스마트 홈 작업에서는 기기 유형을 사용하여 기기에서 어떤 문법을 사용해야 하는지 Google 어시스턴트에 알려줍니다. 기기 특성은 기기 유형의 기능을 정의합니다. 기기는 작업에 추가된 각 기기 특성의 상태를 상속합니다.

dc8dce0dea87cd5c.png

선택한 기기 유형에 지원되는 특성을 연결하여 사용자 기기의 기능을 맞춤설정할 수 있습니다. 현재 기기 스키마에서 사용할 수 없는 작업에 맞춤 특성을 구현하려는 경우 모드전환 속성을 통해 원하는 맞춤 이름을 가진 특정 설정 관련 제어 기능을 구현할 수 있습니다.

Smart Home API에는 유형과 특성별로 제공되는 기본 제어 기능 외에도 더 나은 사용자 경험을 실현하기 위한 추가 기능이 있습니다. 오류 응답은 인텐트가 성공하지 못할 경우 사용자에게 자세한 피드백을 제공합니다. 2단계 인증(2FA)은 이러한 응답을 확장하고 원하는 기기 특성의 보안을 강화합니다. 어시스턴트에서 발생한 챌린지 블록에 구체적인 오류 응답을 전송함으로써 스마트 홈 작업은 명령 수행을 위한 추가 승인을 요청할 수 있습니다.

선행 조건

**빌드**할 항목

이 Codelab에서는 Firebase를 통해 사전 빌드된 스마트 홈 통합을 배포한 다음 세탁량과 터보 모드와 관련하여 스마트 홈 세탁기에 비표준 특성을 추가합니다. 또한 오류 및 예외 보고를 구현하고 2단계 인증을 사용하여 세탁기를 켤 때 음성 확인을 적용하는 방법을 알아봅니다.

학습할 내용

  • 모드 및 전환 속성을 작업에 추가하는 방법
  • 오류 및 예외 보고 방법
  • 2단계 인증 적용 방법

필요한 항목

활동 제어 사용 설정하기

어시스턴트로 사용하려는 Google 계정에서 다음 활동 제어를 사용 설정합니다.

  • 웹 및 앱 활동
  • 기기 정보
  • 음성 및 오디오 활동

작업 프로젝트 만들기

  1. Actions on Google 개발자 콘솔로 이동합니다.
  2. 새 프로젝트를 클릭하고 프로젝트 이름을 입력한 후 프로젝트 만들기를 클릭합니다.

AWXw5E1m9zVgvVeyeL3uxwCX6DtWOCK6LRSLmOATFzjMbmE5cSWBdSVhJZDFpEFH2azZTK2eMs6OYYdMJYiGb5bKqFEzxaLyRUYuwVGBlSjXzTyy8Z9CvwpXvRwP7xdycklETzFc7Q

스마트 홈 앱 선택하기

Actions 콘솔의 개요 화면에서 스마트 홈을 선택합니다.

36RsBUWBgbgsa5xZ7MJVMm1sIg07nXbfjv0mWCxXViaC5SlbL2gMigw9hgXsZQhNMHLLFOfiKdZsSTNXONFB1i47gksw3SBNpkVYl492WeryOlgxKjpVrLAvg-5cZqu1DI-s5kxM3g

스마트 홈 환경 카드를 선택하면 프로젝트 콘솔로 이동하게 됩니다.

pzgHPsmc2LvLoeUvJfkjKQqD_BvO4v8JOPlcrxsmyptFkkjL4PP6LqrM9r5tNvEIfT9HmK-UKw3GWFPXTjqo4nUrhD2o5shUKHBE31OT8iIA69JZCev7_0_nh-lnL2oJHoxGfqqZ4w

Firebase CLI 설치하기

Firebase 명령줄 인터페이스(CLI)를 사용하면 로컬에서 웹 앱을 제공하고 웹 앱을 Firebase 호스팅에 배포할 수 있습니다.

CLI를 설치하려면 터미널에서 다음 npm 명령어를 실행하세요.

npm install -g firebase-tools

CLI가 제대로 설치되었는지 확인하려면 다음을 실행합니다.

firebase --version

다음을 실행하여 Google 계정으로 Firebase CLI를 승인합니다.

firebase login

HomeGraph API 사용 설정

HomeGraph API를 사용하면 사용자의 Home Graph 내에서 기기 및 상태를 저장하고 쿼리할 수 있습니다. 이 API를 사용하려면 먼저 Google Cloud Console을 열고 HomeGraph API를 사용 설정해야 합니다.

Google Cloud Console에서 작업 <project-id>.와 일치하는 프로젝트를 선택합니다. 그런 다음 HomeGraph API의 API 라이브러리 화면에서 사용 설정을 클릭합니다.

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

개발 환경을 설정했으므로 시작 프로젝트를 배포하여 모든 것이 올바르게 구성되었는지 확인할 수 있습니다.

소스 코드 가져오기

다음 링크를 클릭하여 개발 머신에 이 Codelab의 샘플을 다운로드합니다.

소스 코드 다운로드

...또는 명령줄에서 GitHub 저장소를 복제할 수도 있습니다.

git clone https://github.com/googlecodelabs/smarthome-traits.git

다운로드한 ZIP 파일의 압축을 해제합니다.

프로젝트 정보

시작 프로젝트에는 다음과 같은 하위 디렉터리가 포함됩니다.

  • public: 스마트 세탁기의 상태를 쉽게 제어하고 모니터링할 수 있는 프런트엔드 UI입니다.
  • functions: Firebase용 Cloud Functions 및 Firebase 실시간 데이터베이스로 스마트 세탁기를 관리할 수 있으며 완벽하게 구현된 클라우드 서비스입니다.

제공되는 클라우드 처리에는 index.js의 다음 함수가 포함됩니다.

  • fakeauth**:** 계정 연결을 위한 승인 엔드포인트
  • faketoken**:** 계정 연결을 위한 토큰 엔드포인트
  • smarthome**:** 스마트 홈 인텐트 처리 엔드포인트
  • reportstate**:** 기기 상태 변경 시 HomeGraph API 호출
  • requestsync**:** 계정 재연결 요청 없이 사용자 기기 업데이트 사용 설정

Firebase에 연결하기

washer-start 디렉터리로 이동한 후 작업 프로젝트를 사용해 Firebase CLI를 설정합니다.

cd washer-start
firebase use <project-id>

Firebase에 배포

functions 폴더로 이동한 다음 npm.을 사용하여 필요한 모든 종속 항목을 설치합니다.

cd functions
npm install

이제 종속 항목을 설치하고 프로젝트를 구성했으므로 앱을 처음으로 실행할 수 있습니다.

firebase deploy

콘솔에 다음과 같은 결과가 표시됩니다.

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<project-id>.firebaseapp.com

이 명령어는 여러 Firebase용 Cloud Functions와 함께 웹 앱을 배포합니다.

브라우저(https://<project-id>.firebaseapp.com)에서 호스팅 URL을 열어 웹 앱을 확인합니다. 다음과 같은 인터페이스가 표시됩니다.

L60eA7MOnPmbBMl2XMipT9MdnP-RaVjyjf0Y93Y1b7mEyIsqZrrwczE7D3RQISRs-iusL1g4XbNmGhuA6-5sLcWefnczwNJEPfNLtwBsO4Tb9YvcAZBI6_rX19z8rxbik9Vq8F2fwg

이 웹 UI는 기기 상태를 보거나 수정할 수 있는 타사 플랫폼을 나타냅니다. 데이터베이스에 기기 정보를 입력하기 시작하려면 업데이트를 클릭하세요. 페이지에는 변경사항이 표시되지 않지만 세탁기의 현재 상태는 데이터베이스에 저장됩니다.

이제 Actions 콘솔을 사용하여 Google 어시스턴트에 배포한 클라우드 서비스를 연결할 차례입니다.

Actions 콘솔 프로젝트 구성하기

개요 > 작업 빌드에서 작업 추가를 선택합니다. 스마트 홈 인텐트를 처리하는 클라우드 함수의 URL을 입력하고 저장을 클릭합니다.

https://us-central1-<project-id>.cloudfunctions.net/smarthome

Uso-o00XQXBHvOR9vQq9tmpYDYQJKsFEstsgRFnxPAJf7zJ2FxwhISiodo3dB1Tz49Okd6ivi66fjpo7rarS_GZelglGWCT1r9FzDGUl1r67ddIcIbQrxqN8jG9F9GAKOpk0Ckc-eA

개발 > 호출 탭에서 작업의 표시 이름을 추가하고 저장을 클릭합니다. 이 이름이 Google Home 앱에 표시됩니다.

gvC-TvmKDy-D-xjwkeCjNt__9ErA7DL8hZWa1oH1yPJ9SpYOepDYjxx6WnJ56IG-t37fJ65kmHISQdh72Ot2G-0tu6Flxf4gom5kvx_3hlvFeMqYuFgXr_85pfWWn7VLFHtS55p1zw

s4yc1kOW4XtKUQN1EYegiDLU5oTqmxQ2PNbeaujm26OQmYKKpjug7j5FYmutLSAZ1zBd-ZkcZlL7zyTZqw4bge3_oOeWvJTsqJ-A08vfZwImYQrKiquLskLuTpmMqXEZD1xchhCWGQ

작업 연결을 사용하려면 왼쪽 탐색 메뉴에서 개발 > 계정 연결 옵션을 선택합니다. 다음 계정 연결 설정을 사용하세요.

클라이언트 ID

ABC123

클라이언트 비밀번호

DEF456

인증 URL

https://us-central1-.cloudfunctions.net/fakeauth

토큰 URL

https://us-central1-.cloudfunctions.net/faketoken

rRyZTiBSTuPk3YtJtXjDK1pPftUxsaEhs9jzpvFtbHTD6bEwYxM8jV4MWxiljKA1bKVZrIRoO9O3jtBefLKf_OyMpukPjwIj8zGvyU3UwASzMrnRskl-hVAfAmQVi4sC_zAwgYwRXw

저장을 클릭하여 계정 연결 구성을 저장한 후 테스트를 클릭하여 프로젝트에서 테스트를 사용 설정합니다.

OgUvpQfXioygkRwPcaJpzjyNQDZy6enidUC8YMPaCOrZi0YeWCFsCJV9Gqg-_UfsqTnn4KEg--uE3Ymr0QuamDonF4RyYHtRKcULXABDuaEnj2hq8i20LYj1SrGP_1lQ_UsUB90pGw

시뮬레이터로 리디렉션됩니다. 기기에서 테스트(soCeBB1CkSIEqsBmDc8Cth6EjgcXUnrOHeOpLNlvMiiXM73Rmh8iBK1ZFLFd47kycYqIMq3Fm49ryAGUt79BXVPDyEB1IU3W0fgiL49iqTAVrpRszL10mmxzq_AQTJZVrXor-vne2w) 아이콘 위로 마우스를 이동하여 프로젝트의 테스트가 사용 설정되었는지 확인합니다.

2zbfeYpG-wEd2SFP07Wc4mJzHakLX7YvrNw3IV0_0Kd-TonfsKIvvjKWlwvrmTm5jLj3XPWqCtcDd5J2z6gwn9fnchpYVraw1j_mE4M0LVppAl5WY5cK7g0uZyhZ3VFFS25yPmyksg

스마트 홈 작업을 테스트하려면 프로젝트를 Google 계정에 연결해야 합니다. 이렇게 하면 동일한 계정에 로그인된 Google 어시스턴트 표시 경로와 Google Home 앱을 통해 테스트할 수 있습니다.

  1. 휴대전화에서 Google 어시스턴트 설정을 엽니다. 콘솔에서와 동일한 계정으로 로그인해야 합니다.
  2. 어시스턴트에서 Google 어시스턴트 > 설정 > 홈 컨트롤로 이동합니다.
  3. 오른쪽 하단에서 더하기(+) 아이콘을 클릭합니다.
  4. 테스트 앱이 [test] 접두사 및 설정한 표시 이름으로 표시됩니다.
  5. 항목을 선택합니다. 그러면 Google 어시스턴트에서 서비스를 인증하고 SYNC 요청을 보내 서비스에 사용자의 기기 목록을 제공하도록 요청합니다.

Google Home 앱을 열고 세탁기 기기가 표시되는지 확인합니다.

XcWmBVamBZtPfOFqtsr5I38stPWTqDcMfQwbBjetBgxt0FCjEs285pa9K3QXSASptw0KYN2G8yfkT0-xg664V4PjqMreDDs-HPegHjOc4EVtReYPu-WKZyygq9Xmkf8X8z9177nBjQ

Google Home 앱에서 음성 명령을 사용하여 세탁기를 제어할 수 있는지 확인합니다. 클라우드 처리의 프런트엔드 웹 UI에서 기기 상태 변경도 확인할 수 있어야 합니다.

이제 기본 세탁기를 배포했으므로 기기에서 사용 가능한 모드를 맞춤설정할 수 있습니다.

a``ction.devices.traits.Modes 속성을 사용하면 기기에서 하나의 모드에 임의 개수의 설정을 지정할 수 있습니다. 단, 한 번에 하나씩만 설정할 수 있습니다. 세탁기에 모드를 추가하여 소량, 보통, 대량 등 세탁량을 정의합니다.

SYNC 응답 업데이트

functions/index.jsSYNC 응답에 새 특성에 관한 정보를 추가해야 합니다. 이 데이터는 다음 코드 스니펫에 표시된 대로 traits 배열과 attributes 객체에 나타납니다.

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
          // Add Modes trait
          'action.devices.traits.Modes',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          //Add availableModes
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en',
            }],
            settings: [{
              setting_name: 'small',
              setting_values: [{
                setting_synonym: ['small'],
                lang: 'en',
              }]
            }, {
              setting_name: 'medium',
              setting_values: [{
                setting_synonym: ['medium'],
                lang: 'en',
              }]
            }, {
              setting_name: 'large',
              setting_values: [{
                setting_synonym: ['large'],
                lang: 'en',
              }]
            }],
            ordered: true,
          }],
        },
      }],
    },
  };
});

새 EXECUTE 인텐트 명령어 추가

EXECUTE 인텐트에서 다음 코드 스니펫에 표시된 대로 action.devices.commands.SetModes 명령어를 추가합니다.

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      Break;
    // Add SetModes command
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
}

QUERY 응답 업데이트

그런 다음 QUERY 응답을 업데이트하여 세탁기의 현재 상태를 보고합니다.

queryFirebasequeryDevice 함수에 업데이트된 변경사항을 추가하여 실시간 데이터베이스에 저장된 상태를 가져옵니다.

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
    // Add Modes snapshot
    load: snapshotVal.Modes.load,
  };
}

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    // Add currentModeSettings
    currentModeSettings: {
      load: data.load,
    },
  };
};

보고서 상태 업데이트

마지막으로 reportstate 함수를 업데이트하여 세탁기의 현재 세탁량 설정을 홈 그래프에 보고합니다.

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          // Add currentModeSettings
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
        },
      },
    },
  },
};

Firebase에 배포

다음 명령어를 실행하여 업데이트된 작업을 배포합니다.

firebase deploy --only functions

배포가 완료되면 웹 UI로 이동하여 툴바에서 새로고침 ae8d3b25777a5e30.png 버튼을 클릭합니다. 이렇게 하면 요청 동기화가 트리거되기 때문에 어시스턴트가 업데이트된 SYNC 응답 데이터를 수신합니다.

bf4f6a866160a982.png

이제 다음과 같은 명령어를 말하여 세탁기 모드를 설정할 수 있습니다.

"Hey Google, 세탁량을 대량으로 설정해 줘."

또한 다음과 같이 세탁기에 관해 질문할 수도 있습니다.

"Hey Google, 현재 세탁량은?"

action.devices.traits.Toggles 특성은 세탁기가 터보 모드인지와 같이 true 또는 false 상태로 답변이 가능한 기기의 측면을 나타내며, 각 측면에는 이름이 지정되어 있습니다.

SYNC 응답 업데이트

SYNC 응답에서 새 기기 특성에 관한 정보를 추가해야 합니다. 다음 코드 스니펫에서와 같이 traits 배열과 attributes 객체에 나타납니다.

index.js

app.onSync(body => {
  return {
    requestId: 'ff36a3cc-ec34-11e6-b1a0-64510650abcf',
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
          'action.devices.traits.Modes',
          // Add Toggles trait
          'action.devices.traits.Toggles',
        ],
        name: { ... },
        deviceInfo: { ... },
        attributes: {
          pausable: true,
          availableModes: [{
            name: 'load',
            name_values: [{
              name_synonym: ['load'],
              lang: 'en'
            }],
            settings: [{ ... }],
            ordered: true,
          }],
          //Add availableToggles
          availableToggles: [{
            name: 'Turbo',
            name_values: [{
              name_synonym: ['turbo'],
              lang: 'en',
            }],
          }],
        },
      }],
    },
  };
});

새 EXECUTE 인텐트 명령어 추가

EXECUTE 인텐트에서 다음 코드 스니펫에 표시된 대로 action.devices.commands.SetToggles 명령어를 추가합니다.

index.js

const updateDevice = async (execution,deviceId) => {
  const {params,command} = execution;
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.SetModes':
      state = {load: params.updateModeSettings.load};
      ref = firebaseRef.child(deviceId).child('Modes');
      break;
    // Add SetToggles command
    case 'action.devices.commands.SetToggles':
      state = {Turbo: params.updateToggleSettings.Turbo};
      ref = firebaseRef.child(deviceId).child('Toggles');
      break;
  }

QUERY 응답 업데이트

마지막으로 세탁기의 터보 모드를 보고하려면 QUERY 응답을 업데이트해야 합니다. queryFirebasequeryDevice 함수에 업데이트된 변경사항을 추가하여 실시간 데이터베이스에 저장된 전환 상태를 가져옵니다.

index.js

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
    load: snapshotVal.Modes.load,
    // Add Toggles snapshot
    Turbo: snapshotVal.Toggles.Turbo,
  };
}

const queryDevice = async (deviceId) => {
  const data = queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{ ... }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
    currentModeSettings: {
      load: data.load,
    },
    // Add currentToggleSettings
    currentToggleSettings: {
      Turbo: data.Turbo,
    },
  };
};

보고서 상태 업데이트

마지막으로 reportstate 함수를 업데이트하여 세탁기가 터보 모드로 설정되어 있는지를 홈 그래프에 보고합니다.

index.js

const requestBody = {
  requestId: 'ff36a3cc', /* Any unique ID */
  agentUserId: USER_ID,
  payload: {
    devices: {
      states: {
        /* Report the current state of your washer */
        [context.params.deviceId]: {
          on: snapshot.OnOff.on,
          isPaused: snapshot.StartStop.isPaused,
          isRunning: snapshot.StartStop.isRunning,
          currentModeSettings: {
            load: snapshot.Modes.load,
          },
          // Add currentToggleSettings
          currentToggleSettings: {
            Turbo: snapshot.Toggles.Turbo,
          },
        },
      },
    },
  },
};

Firebase에 배포

다음 명령어를 실행하여 업데이트된 함수를 배포합니다.

firebase deploy --only functions

배포가 완료된 후 웹 UI에서 새로고침 ae8d3b25777a5e30.png 버튼을 클릭하여 동기화 요청을 트리거합니다.

이제 다음과 같이 말하여 세탁기를 터보 모드로 설정할 수 있습니다.

"Hey Google, 세탁기 터보 모드 켜 줘."

다음과 같이 질문하여 세탁기가 터보 모드인지 확인할 수도 있습니다.

"Hey Google, 지금 세탁기 터보 모드야?"

스마트 홈 작업 내 오류 처리를 통해 EXECUTEQUERY 응답이 실패할 경우 사용자에게 이를 보고할 수 있습니다. 알림은 사용자가 스마트 기기 및 작업과 상호작용할 때 사용자 경험을 더욱 긍정적으로 만드는 데 도움이 됩니다.

EXECUTE 또는 QUERY 요청이 실패하면 작업에서 오류 코드를 반환해야 합니다. 예를 들어 사용자가 덮개를 연 상태에서 세탁기를 시작하려 하여 오류가 발생하는 경우 EXECUTE 응답은 다음 코드 스니펫과 비슷할 수 있습니다.

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [
      {
        "ids": [
          "456"
        ],
        "status": "ERROR",
        "errorCode": "deviceLidOpen"
      }
    ]
  }
}

이제 사용자가 세탁기 시작을 요청하면 어시스턴트는 다음과 같이 응답합니다.

"세탁기 덮개가 열려 있습니다. 덮개를 닫고 다시 시도해 보세요."

예외는 오류와 비슷하지만 알림이 명령어와 연관이 있는 경우로 실행을 차단할 수도 있고 그러지 않을 수도 있습니다. 예외는 StatusReport 특성을 사용하여 배터리 수준 또는 최근 상태 변경 등 관련 정보를 제공할 수 있습니다. 실행을 차단하지 않는 예외 코드는 SUCCESS 상태와 함께 반환되는 반면 실행을 차단하는 예외 코드는 EXCEPTIONS 상태와 함께 반환됩니다.

예외가 있는 응답 예시는 다음 코드 스니펫에 나와 있습니다.

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "commands": [{
      "ids": ["123"],
      "status": "SUCCESS",
      "states": {
        "online": true,
        "isPaused": false,
        "isRunning": false,
        "exceptionCode": "runCycleFinished"
      }
    }]
  }
}

어시스턴트는 다음과 같이 응답합니다.

"세탁기 가동이 완료되었습니다."

세탁기에 관한 오류 보고를 추가하려면 functions/index.js를 열고 다음 코드 스니펫과 같이 오류 클래스 정의를 추가합니다.

index.js

app.onQuery(async (body) => {...});

// Add SmartHome error handling
class SmartHomeError extends Error {
  constructor(errorCode, message) {
    super(message);
    this.name = this.constructor.name;
    this.errorCode = errorCode;
  }
}

오류 코드와 오류 상태를 반환하도록 실행 응답을 업데이트합니다.

index.js

const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
  for (const device of command.devices) {
    for (const execution of command.execution) {
      executePromises.push( ... )
          //Add error response handling
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
            }
          })
      );
    }
  }
}

이제 어시스턴트는 개발자가 보고한 오류 코드에 관해 사용자에게 알릴 수 있습니다. 다음 섹션에서 구체적인 예를 확인할 수 있습니다.

기기에 보안이 필요하거나 승인된 일부 사용자만 액세스할 수 있도록 제한해야 하는 모드가 있다면 작업(예: 소프트웨어 업데이트, 잠금 해제)에 2단계 인증(2FA)을 구현해야 합니다.

보안 챌린지를 매번 실행할지 또는 특정 기준이 충족되어야 하는지 등을 맞춤설정하여 모든 기기 유형과 특성에 2FA를 구현할 수 있습니다.

다음과 같이 세 가지 챌린지 유형이 지원됩니다.

  • No challenge: 인증 챌린지를 사용하지 않는 요청 및 응답(기본 동작)
  • ackNeeded: 명시적 확인이 필요한 2FA(예 또는 아니요)
  • pinNeeded: PIN이 필요한 2FA

이 Codelab에서는 세탁기를 켜는 명령어에 ackNeeded 챌린지를 추가하고 2단계 인증이 실패한 경우 오류를 반환하는 기능을 추가합니다.

functions/index.js를 열고 다음 코드 스니펫과 같이 오류 코드 및 챌린지 유형을 반환하는 오류 클래스 정의를 추가합니다.

index.js

class SmartHomeError extends Error { ... }

// Add 2FA error handling
class ChallengeNeededError extends SmartHomeError {
  constructor(tfaType) {
    super('challengeNeeded', tfaType);
    this.tfaType = tfaType;
  }
}

또한 실행 응답을 업데이트하여 다음과 같이 challengeNeeded 오류를 반환해야 합니다.

index.js

const executePromises = [];
const intent = body.inputs[0];
for (const command of intent.payload.commands) {
  for (const device of command.devices) {
    for (const execution of command.execution) {
      executePromises.push( ... )
          .catch((error) => {
            functions.logger.error('EXECUTE', device.id, error);
            result.ids.push(device.id);
            if(error instanceof SmartHomeError) {
              result.status = 'ERROR';
              result.errorCode = error.errorCode;
              //Add 2FA error handling
              if(error instanceof ChallengeNeededError) {
                result.challengeNeeded = {
                  type: error.tfaType
                };
              }
            }
          })
      );
    }
  }
}

마지막으로 updateDevice를 수정하여 세탁기를 켜거나 끄려고 할 때 명시적 확인을 요청합니다.

index.js

const updateDevice = async (execution,deviceId) => {
  const {challenge,params,command} = execution; //Add 2FA challenge
  let state, ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      //Add 2FA challenge
      if (!challenge || !challenge.ack) {
        throw new ChallengeNeededError('ackNeeded');
      }
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    ...
  }

  return ref.update(state)
      .then(() => state);
};

Firebase에 배포

다음 명령어를 실행하여 업데이트된 함수를 배포합니다.

firebase deploy --only functions

업데이트된 코드를 배포한 후 다음과 같이 어시스턴트에 세탁기를 켜거나 끄도록 요청하여 작업을 음성으로 확인해야 합니다.

나: "Hey Google, 세탁기 켜 줘."

어시스턴트: "세탁기를 켜시겠어요?"

나: "응"

또한 Firebase 로그를 열어 2FA 흐름의 각 단계에 관한 자세한 응답을 확인할 수 있습니다.

289dbe48f4bb8106.png

674c4f4392e98c1.png

축하합니다. ModesToggles 특성을 통해 스마트 홈 작업의 기능을 확장하고 2단계 인증을 통해 실행의 보안을 강화했습니다.

자세히 알아보기

다음은 작업을 좀 더 활용하기 위해 구현할 수 있는 몇 가지 아이디어입니다.

  • 기기에 로컬 실행 기능을 추가합니다.
  • 다른 2단계 인증 챌린지 유형을 사용하여 기기 상태를 수정합니다.
  • RunCycle 속성 QUERY 응답을 업데이트하여 동적으로 업데이트합니다.
  • 이 GitHub 샘플을 살펴봅니다.