Google 어시스턴트에 스마트 홈 기기 연결하기

사물 인터넷 (IoT) 개발자는 사용자가 Google Home 앱의 터치 컨트롤 및 어시스턴트의 음성 명령을 통해 기기를 제어할 수 있는 스마트 홈 작업를 빌드할 수 있습니다.

79266e5f45e6ae20.gif

스마트 홈 작업은 집과 기기에 관한 문맥 데이터를 제공하여 집의 논리적 지도를 만드는 Home Graph를 바탕으로 합니다. 어시스턴트는 이러한 문맥 정보를 기반으로 집에 있는 각 사용자의 위치를 사용하여 사용자의 요청을 더 잘 이해할 수 있습니다. 예를 들어 Home Graph는 온도 조절기, 전등, 환풍기, 진공청소기 등 여러 제조업체에서 만든 다양한 유형의 기기가 포함된 거실의 개념을 저장할 수 있습니다.

d009cef0f903d284.jpeg

선행 조건

빌드할 항목

이 Codelab에서는 가상의 스마트 세탁기를 관리하는 클라우드 서비스를 게시한 후 스마트 홈 작업을 빌드하고 어시스턴트에 연결합니다.

과정 내용

  • 스마트 홈 클라우드 서비스를 배포하는 방법
  • 어시스턴트에 서비스를 연결하는 방법
  • 기기 상태 변경사항을 Google에 게시하는 방법

준비물

활동 제어 사용 설정하기

어시스턴트로 사용하려는 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

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

소스 코드 가져오기

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

소스 코드 다운로드

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

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

프로젝트 정보

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

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

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-<project-id>.cloudfunctions.net/fakeauth

토큰 URL

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

rRyZTiBSTuPk3YtJtXjDK1pPftUxsaEhs9jzpvFtbHTD6bEwYxM8jV4MWxiljKA1bKVZrIRoO9O3jtBefLKf_OyMpukPjwIj8zGvyU3UwASzMrnRskl-hVAfAmQVi4sC_zAwgYwRXw

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

OgUvpQfXioygkRwPcaJpzjyNQDZy6enidUC8YMPaCOrZi0YeWCFsCJV9Gqg-_UfsqTnn4KEg--uE3Ymr0QuamDonF4RyYHtRKcULXABDuaEnj2hq8i20LYj1SrGP_1lQ_UsUB90pGw

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

2zbfeYpG-wEd2SFP07Wc4mJzHakLX7YvrNw3IV0_0Kd-TonfsKIvvjKWlwvrmTm5jLj3XPWqCtcDd5J2z6gwn9fnchpYVraw1j_mE4M0LVppAl5WY5cK7g0uZyhZ3VFFS25yPmyksg

이제 기기 상태를 어시스턴트와 연결하는 데 필요한 웹훅을 구현할 수 있습니다.

이제 작업을 구성했으므로 기기를 추가하고 데이터를 전송할 수 있습니다. 클라우드 서비스에서는 다음 인텐트를 처리해야 합니다.

  • SYNC 인텐트는 어시스턴트가 사용자가 연결한 기기를 알고자 할 때 발생합니다. 이 인텐트는 사용자가 계정을 연결하면 서비스로 전송됩니다. 사용자의 모든 기기 및 기기 기능이 포함된 JSON 페이로드로 응답해야 합니다.
  • QUERY 인텐트는 어시스턴트가 현재 상태나 기기의 상태를 알고자 할 때 발생합니다. 요청된 각 기기의 상태가 포함된 JSON 페이로드로 응답해야 합니다.
  • EXECUTE 인텐트는 어시스턴트가 사용자를 대신하여 기기를 제어하려고 할 때 발생합니다. 요청된 각 기기의 실행 상태가 포함된 JSON 페이로드로 응답해야 합니다.
  • DISCONNECT 인텐트는 사용자가 어시스턴트에서 계정을 연결 해제할 때 발생합니다. 이 사용자 기기의 이벤트를 어시스턴트로 전송하는 것을 중지해야 합니다.

다음 섹션에서 이러한 인텐트를 처리하기 위해 이전에 배포한 함수를 업데이트하게 됩니다.

SYNC 응답 업데이트하기

어시스턴트 요청에 응답하기 위한 코드가 포함된 functions/index.js를 엽니다.

기기 메타데이터 및 기능을 반환하여 SYNC 인텐트를 처리해야 합니다. 기기 정보와 세탁기용 권장 특성을 포함하도록 onSync 배열에 있는 JSON을 업데이트합니다.

index.js

app.onSync((body) => {
  return {
    requestId: body.requestId,
    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',
        ],
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['Washer'],
        },
        deviceInfo: {
          manufacturer: 'Acme Co',
          model: 'acme-washer',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          pausable: true,
        },
      }],
    },
  };
});

Firebase에 배포하기

Firebase CLI를 사용하여 업데이트된 클라우드 처리를 배포합니다.

firebase deploy --only functions

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

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

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

XcWmBVamBZtPfOFqtsr5I38stPWTqDcMfQwbBjetBgxt0FCjEs285pa9K3QXSASptw0KYN2G8yfkT0-xg664V4PjqMreDDs-HPegHjOc4EVtReYPu-WKZyygq9Xmkf8X8z9177nBjQ

이제 클라우드 서비스에서 세탁기 기기를 Google에 적절하게 보고하므로 기기 상태를 요청하고 명령어를 전송할 수 있어야 합니다.

QUERY 인텐트 처리하기

QUERY 인텐트에는 일련의 기기가 포함됩니다. 기기마다 현재 상태로 응답해야 합니다.

functions/index.js에서 QUERY 핸들러를 수정하여 인텐트 요청에 포함된 대상 기기 목록을 처리합니다.

index.js

app.onQuery(async (body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  const intent = body.inputs[0];
  for (const device of intent.payload.devices) {
    const deviceId = device.id;
    queryPromises.push(queryDevice(deviceId)
        .then((data) => {
        // Add response to device payload
          payload.devices[deviceId] = data;
        }
        ));
  }
  // Wait for all promises to resolve
  await Promise.all(queryPromises);
  return {
    requestId: requestId,
    payload: payload,
  };
});

요청에 포함된 기기마다 실시간 데이터베이스에 저장된 현재 상태를 반환합니다. 세탁기의 상태 데이터를 반환하도록 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,
  };
};

const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);
  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
  };
};

EXECUTE 인텐트 처리하기

EXECUTE 인텐트는 기기 상태를 업데이트하는 명령어를 처리합니다. 응답은 각 명령어의 상태(예: SUCCESS, ERROR 또는 PENDING)와 새 기기 상태를 반환합니다.

업데이트가 필요한 특성 목록 및 각 명령어의 대상 기기 세트를 처리하도록 functions/index.js에서 EXECUTE 핸들러를 수정합니다.

index.js

app.onExecute(async (body) => {
  const {requestId} = body;
  // Execution results are grouped by status
  const result = {
    ids: [],
    status: 'SUCCESS',
    states: {
      online: true,
    },
  };

  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(
            updateDevice(execution, device.id)
                .then((data) => {
                  result.ids.push(device.id);
                  Object.assign(result.states, data);
                })
                .catch(() => functions.logger.error('EXECUTE', device.id)));
      }
    }
  }

  await Promise.all(executePromises);
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

명령어 및 대상 기기마다 실시간 데이터베이스에서 요청된 특성에 상응하는 값을 업데이트합니다. 적절한 Firebase 참조를 업데이트하고 업데이트된 기기 상태를 반환하도록 updateDevice 함수를 수정합니다.

index.js

const updateDevice = async (execution, deviceId) => {
  const {params, command} = execution;
  let state; let 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;
  }

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

세 가지 인텐트를 모두 구현한 후에는 작업이 세탁기를 제어하는지 테스트할 수 있습니다.

Firebase에 배포하기

Firebase CLI를 사용하여 업데이트된 클라우드 처리를 배포합니다.

firebase deploy --only functions

세탁기 테스트하기

이제 휴대전화를 통해 다음 음성 명령을 시도하면 값이 변경된 것을 확인할 수 있습니다.

"Hey Google, 세탁기 켜 줘."

"Hey Google, 세탁기 일시중지해 줘."

"Hey Google, 세탁기 중지해 줘."

또한 질문을 통해 세탁기의 현재 상태를 확인할 수 있습니다.

"Hey Google, 세탁기 켜져 있어?"

"Hey Google, 내 세탁기 돌아가고 있어?"

"Hey Google, 세탁기가 지금 무슨 모드야?"

Firebase Console의 탐색 메뉴에서 개발 > 함수 > 로그를 클릭하여 다음의 쿼리와 명령어를 확인할 수 있습니다.

ce02fb66fe2037dc.png

사용자가 기기의 현재 상태를 제어하고 쿼리할 수 있도록 클라우드 서비스를 스마트 홈 인텐트와 완전히 통합했습니다. 하지만 이 구현에는 서비스가 기기 정보 또는 상태의 변경과 같은 이벤트 정보를 어시스턴트에 사전에 전송할 수 있는 방법이 없습니다.

사용자가 기기를 추가 또는 삭제하거나 기기 기능이 변경되면 동기화 요청을 사용해 새 동기화 요청을 트리거할 수 있습니다. 사용자가 실제로 기기 상태를 변경(예: 조명 스위치를 켬)하거나 다른 서비스를 사용해 상태를 변경했을 때 보고서 상태를 사용해 클라우드 서비스에서 사전에 기기 상태를 Home Graph에 전송할 수 있습니다.

이 섹션에서는 프런트엔드 웹 앱에서 이러한 메서드를 호출하기 위한 코드를 추가합니다.

HomeGraph API 사용 설정하기

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

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

5SVCzM8IZLi_9DV8M0nEklv16NXkpvM0bIzQK2hSyKyvnFHBxPOz90rbr72ayxzmxd5aNROOqC_Cp4outbdlwJdObDs0DIE_8vYzw6dovoVrP9IZWlWsZxDS7UHOi1jiRbDMG8MqUA

보고서 상태 사용 설정하기

실시간 데이터베이스에 작성하면 시작 프로젝트에서 reportstate 함수가 트리거됩니다. 데이터베이스에 기록된 데이터를 캡처하고 보고서 상태를 통해 Home Graph에 게시하도록 functions/index.jsreportstate 함수를 업데이트합니다.

index.js

exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      functions.logger.info('Firebase write event triggered Report State');
      const snapshot = change.after.val();

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

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody,
      });
      functions.logger.info('Report state response:', res.status, res.data);
    });

동기화 요청 사용하기

프런트엔드 웹 UI에서 아이콘을 새로고침하면 시작 프로젝트에서 requestsync 함수가 트리거됩니다. HomeGraph API를 호출하도록 functions/index.js에서 requestsync 함수를 구현합니다.

index.js

exports.requestsync = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  functions.logger.info(`Request SYNC for user ${USER_ID}`);
  try {
    const res = await homegraph.devices.requestSync({
      requestBody: {
        agentUserId: USER_ID,
      },
    });
    functions.logger.info('Request sync response:', res.status, res.data);
    response.json(res.data);
  } catch (err) {
    functions.logger.error(err);
    response.status(500).send(`Error requesting sync: ${err}`);
  }
});

Firebase에 배포하기

Firebase CLI를 사용하여 업데이트된 코드를 배포합니다.

firebase deploy --only functions

구현 테스트하기

웹 UI에서 새로고침 ae8d3b25777a5e30.png 버튼을 클릭하고 Firebase Console 로그에 동기화 요청이 표시되는지 확인합니다.

5241d663238a8d04.png

그런 다음 프런트엔드 웹 UI에서 세탁기 기기의 속성을 조정하고 업데이트를 클릭합니다. Firebase Console 로그에서 Google에 보고된 상태 변경사항을 볼 수 있는지 확인합니다.

674c4f4392e98c1.png

축하합니다. 스마트 홈 작업을 사용하여 어시스턴트를 기기 클라우드 서비스와 통합하셨습니다.

자세히 알아보기

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

또한 사용자에게 작업을 게시하기 위한 인증 프로세스 등 작업을 테스트 및 제출하는 과정도 자세히 알아볼 수 있습니다.