<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Dave Smith on Medium]]></title>
        <description><![CDATA[Stories by Dave Smith on Medium]]></description>
        <link>https://medium.com/@devunwired?source=rss-c91ac3b63127------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*TuZqGHjlqC9prSC4lDD3xw.jpeg</url>
            <title>Stories by Dave Smith on Medium</title>
            <link>https://medium.com/@devunwired?source=rss-c91ac3b63127------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 29 Sep 2019 04:02:12 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/@devunwired" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Smart Home Cloud Services with Google: Part 2]]></title>
            <link>https://medium.com/google-developers/smart-home-cloud-services-with-google-part-2-3901ab39c39c?source=rss-c91ac3b63127------2</link>
            <guid isPermaLink="false">https://medium.com/p/3901ab39c39c</guid>
            <category><![CDATA[actions-on-google]]></category>
            <category><![CDATA[google-assistant]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[Dave Smith]]></dc:creator>
            <pubDate>Fri, 27 Sep 2019 15:31:01 GMT</pubDate>
            <atom:updated>2019-09-27T15:31:01.155Z</atom:updated>
            <content:encoded><![CDATA[<p>In the <a href="https://medium.com/google-developers/building-a-smart-home-cloud-service-with-google-1ee436ac5a03">previous post of this series</a>, we explored using Cloud IoT Core and Firebase to build a device cloud for smart home devices. We saw how Cloud IoT Core enables us to securely connect constrained devices to Google Cloud, while Firebase constructs a user framework around our device data. As a quick review, here is the cloud service architecture we discussed last time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*XUxdYux1b1GRPxyd" /><figcaption><em>Figure 1: Cloud service architecture</em></figcaption></figure><p>Now, let’s look at extending this cloud service to integrate with the Google Assistant through <a href="https://developers.google.com/actions/smarthome/">smart home Actions</a>. This enables users to link their account through the Google Home app and control their devices through any Assistant-enabled surface.</p><blockquote>If you are unfamiliar with Actions on Google or smart home Actions for the Google Assistant. I recommend reading <strong>IoT &amp; Google Assistant</strong> <a href="https://medium.com/google-developers/iot-google-assistant-f0908f354681">part 1</a> and <a href="https://medium.com/google-developers/jdanielmyers-smart-home-eac8f87fd56e">part 2</a> by my colleague, Dan Myers, as a starting point.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*DF4m1mpZkf3VO0PXqD5z4w.png" /><figcaption><em>Figure 2: Example devices registered in Home App</em></figcaption></figure><p>As a developer, this brings the power of the Home Graph to your devices and gives them context within the user’s home. This context is what enables users to make natural requests, like “What is the temperature in the hallway?”, instead of referring to the device by name.</p><p>To build a smart home Action, create a new project in the <a href="https://console.actions.google.com/">Actions console</a>. We will add two new features to our device cloud service and configure them in our console project: account linking and intent fulfillment. Let’s start by taking a look at how to integrate with the account linking process.</p><blockquote>You can find the sample code described in this post on <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud">GitHub</a>.</blockquote><h3>Account linking</h3><p>Users authorize the Google Assistant to access their devices through account linking. This process enables the user to sign in to the account they use with the device cloud and connect the device managed by that account to Google. The Actions on Google platform supports several different account linking flows, but only the <a href="https://developers.google.com/actions/identity/oauth2?oauth=code">OAuth 2.0 Authorization Code</a> flow is supported for smart home Actions.</p><p>To configure OAuth account linking, you need to supply two endpoints in the Actions console: one for <strong>authorization </strong>and the other for <strong>token </strong>exchange. The authorization endpoint is a web UI where the user can authenticate and agree to link their account with the Google Assistant. It must return an authorization code that uniquely identifies the user.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*eeQlrmSjUQ9M46cvYAc__Q.png" /><figcaption><em>Figure 3: Example account linking UI</em></figcaption></figure><p>Since we are using Firebase Authentication for the client apps, we can add a new Angular route to our web client for the user to sign-in and return their <a href="https://firebase.google.com/docs/auth/admin/verify-id-tokens">Firebase ID token</a> as the authorization code once they authorize access:</p><pre>export class LinkAccountComponent implements OnInit {<br>  redirectUri: string;<br>  state: string;<br>  idToken: string;<br>  constructor(private authService: AngularFireAuth,<br>    private route: ActivatedRoute) { }</pre><pre>ngOnInit() {<br><strong>    this.authService.idToken.subscribe((token) =&gt; {<br>      this.idToken = token;<br></strong>    <strong>}</strong>);</pre><pre>    this.route.queryParamMap.subscribe((params) =&gt; {<br>      this.redirectUri = params.get(&#39;redirect_uri&#39;);<br>      this.state = params.get(&#39;state&#39;);<br>    });<br>  }</pre><pre>linkAccount() {<br><strong>    const next = new URL(this.redirectUri);<br>    next.searchParams.append(&#39;code&#39;, this.idToken);<br>    next.searchParams.append(&#39;state&#39;, this.state);<br>    window.location.href = next.toString();</strong><br>  }<br>}</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/web/src/app/link.component.ts">web/src/app/link.component.ts</a></blockquote><p>Once the authorization flow is complete, the Google Assistant calls your token endpoint to exchange the authorization code for a persistent refresh token. This token does not expire and remains valid unless the user chooses to revoke device access or unlink their account.</p><p>Using the Firebase Admin SDK, we can validate and decode the ID token to obtain the UID of the user. If the token is valid, we can generate a refresh token and associate it with the user’s UID in Firestore. This enables us to look up the token again later for validating future requests.</p><pre>async function handleAuthorizationCode(request, response) {<br><strong>  // Auth code is a Firebase ID token<br>  const decodedToken = await auth.verifyIdToken(request.body.code);</strong><br>  // Verify UID exists in our database<br>  const result = await auth.getUser(decodedToken.uid);</pre><pre>  // Encode the user info as a JWT<br>  const refresh = jwt.sign({<br>    sub: result.uid,<br>    aud: client_id<br>  }, secret);<br><br><strong>  // Register this refresh token for the given user<br>  const userRef = firestore.doc(`users/${result.uid}`);<br>  await userRef.set({ &#39;refresh_token&#39;: refresh });</strong></pre><pre>  ...<br>}</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/token.js">functions/smart-home/token.js</a></blockquote><p>Firebase Authentication is an identity provider, but not a complete OAuth solution. This means our device cloud service must augment Firebase by minting and verifying tokens used for access to user data. The example code uses the <a href="https://jwt.io/">JWT</a> standard to create a self-encoded token with the following JSON payload:</p><pre>{<br>  &quot;sub&quot;: uid,<br>  &quot;aud&quot;: client_id<br>  &quot;iat&quot;: issued_at_time<br>}</pre><blockquote>We are using the JWT.io <a href="https://jwt.io/#libraries-io">library</a> for Node.js for all operations related to generating and validating tokens in this example.</blockquote><p>The Google Assistant uses this refresh token to request an access token that will authenticate requests for device data. Our example service validates the refresh token signature and checks to make sure it’s the refresh token we expect for that user.</p><pre>async function handleRefreshToken(request, response) {<br>  const refreshToken = request.body.refresh_token;<br>  // Verify UID exists in our database<br>  <strong>const decodedToken = jwt.verify(refreshToken, secret);</strong><br>  const result = await auth.getUser(decodedToken.sub);</pre><pre>  // Verify incoming token matches our stored refresh token<br>  const userRef = firestore.doc(`users/${result.uid}`);<br>  const user = await userRef.get();<br>  const validToken = user.data().refresh_token;<br>  if (validToken !== refreshToken) throw new Error(...);</pre><pre>  <strong>// Obtain a new access token<br>  const access = jwt.sign({<br>    sub: result.uid,<br>    aud: client_id<br>  }, secret, {<br>      expiresIn: &#39;1h&#39;<br>    });</strong></pre><pre>  ...<br>}</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/token.js">functions/smart-home/token.js</a></blockquote><p>OAuth access tokens should expire, which requires the Assistant service to periodically request a new one using the persistent refresh token. This enables the user to revoke their authorization if necessary. The example access tokens contain the same self-encoded payload as the refresh token, but they expire after one hour.</p><h3>Intent fulfillment</h3><p>With the authorization and token endpoints in place, we are ready to begin implementing the fulfillment logic for the user’s devices. In this post, we will focus on implementing each intent in the context of our device cloud example, but you can find additional details on these intents and how they work together in the <a href="https://developers.google.com/actions/smarthome/concepts/intents">documentation</a>.</p><p>We can use the <a href="https://actions-on-google.github.io/actions-on-google-nodejs/">Actions on Google Client Library</a> for Node.js, which handles parsing the fulfillment requests and provides individual callbacks to handle each intent.</p><pre><strong>const { smarthome } = require(&#39;actions-on-google&#39;);<br>const fulfillment = smarthome();</strong></pre><pre>/** SYNC Intent Handler */<br>fulfillment.onSync(async (body, headers) =&gt; {<br>  ...<br>});</pre><pre>/** QUERY Intent Handler */<br>fulfillment.onQuery(async (body, headers) =&gt; {<br>  ...<br>});</pre><pre>/** EXECUTE Intent Handler */<br>fulfillment.onExecute(async (body, headers) =&gt; {<br>  ...<br>});</pre><pre>/** DISCONNECT Intent Handler */<br>fulfillment.onDisconnect(async (body, headers) =&gt; {<br>  ...<br>});</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><p>At the beginning of each handler, we need to validate the access token provided with the request. Since the access tokens our application provides are formatted as a JWT that has the user’s UID encoded inside, we simply need to verify the JWT signature using our application’s secret to ensure the token came from us, and check that it has not expired. All of this is handled automatically by the verify() method of the JWT.io <a href="https://jwt.io/#libraries-io">client library</a> for Node.js.</p><pre><strong>const jwt = require(&#39;jsonwebtoken&#39;);</strong></pre><pre>/**<br> * Verify the request credentials provided by the caller.<br> * If successful, return UID encoded in the token.<br> */<br>function validateCredentials(headers, jwt_secret) {<br>  if (!headers.authorization ||<br>      !headers.authorization.startsWith(&#39;Bearer &#39;)) {<br>    throw new Error(&#39;Request missing valid authorization&#39;);<br>  }</pre><pre>  var token = headers.authorization.split(&#39;Bearer &#39;)[1];<br>  <strong>var decoded = jwt.verify(token, jwt_secret);</strong></pre><pre>  return decoded.sub;<br>}</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><p>If the provided token is valid, the method will return the UID, which we will need in the intent handlers to query the proper device data. Let’s examine how our device cloud can interact with each intent: <strong>SYNC</strong>, <strong>QUERY</strong>, <strong>EXECUTE</strong>, and <strong>DISCONNECT</strong>.</p><h3>SYNC</h3><p>The Google Assistant sends a <a href="https://developers.google.com/actions/smarthome/create#actiondevicessync"><strong>SYNC</strong></a> intent after account linking succeeds to request the list of available devices. The response tells the Google Assistant which devices are owned by the given user and their capabilities (also known as <a href="https://developers.google.com/actions/smarthome/traits/">traits</a>) of each device. This includes an identifier to represent the user (<strong>agentUserId</strong>) and a unique id for each device.</p><p>For the device cloud sample project, this means returning the list of metadata for all devices where the user’s UID is set as the owner.</p><pre>fulfillment.onSync(async (body, headers) =&gt; {<br>  const userId = validateCredentials(headers);<br>  <strong>// Return all devices registered to the requested user<br>  const result = await firestore.collection(&#39;devices&#39;)<br>    .where(&#39;owner&#39;, &#39;==&#39;, userId).get();</strong><br>  const deviceList = [];<br>  result.forEach(doc =&gt; {<br>    const device = new Device(doc.id, doc.data());<br>    deviceList.push(device.metadata);<br>  });</pre><pre>  return {<br>    requestId: body.requestId,<br>    payload: {<br>      agentUserId: userId,<br>      devices: deviceList<br>    }<br>  };<br>});</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><p>The <strong>SYNC</strong> response only contains the device types and their capabilities; it does not report any device state. Below is an example device entry in the <strong>SYNC</strong> response payload for a <a href="https://developers.google.com/actions/smarthome/guides/light">light bulb</a> and <a href="https://developers.google.com/actions/smarthome/guides/thermostat">thermostat</a>:</p><pre>{<br>  id: &#39;light-123abc&#39;,<br>  type: &#39;action.devices.types.LIGHT&#39;,<br>  traits: [<br>    &#39;action.devices.traits.OnOff&#39;,<br>    &#39;action.devices.traits.Brightness&#39;<br>  ],<br>  name: {<br>    name: &#39;Kitchen Light&#39;<br>  },<br>  willReportState: true<br>},<br>{<br>  id: &#39;thermostat-123abc&#39;,<br>  type: &#39;action.devices.types.THERMOSTAT&#39;,<br>  traits: [<br>    &#39;action.devices.traits.TemperatureSetting&#39;<br>  ],<br>  attributes: {<br>    availableThermostatModes: &#39;off,heat,cool&#39;,<br>    thermostatTemperatureUnit: &#39;C&#39;<br>  },<br>  name: {<br>    name: &#39;Hallway Thermostat&#39;<br>  },<br>  willReportState: true<br>}</pre><h3>Request Sync</h3><p>When users add or remove devices associated with their account, you should notify the Google Assistant through the Home Graph API via <a href="https://developers.google.com/actions/smarthome/request-sync">Request Sync</a>. Without this feature in your service, users must unlink and relink their account to see changes or explicitly say “Hey Google, sync my devices”. Calling the request sync API triggers a new <strong>SYNC</strong> intent to allow your service to provide updated device information.</p><p>In our example, we can observe when a device node is added or removed in Firestore, and request a sync in each instance. The HomeGraph API will throw an error if that user has not linked their account, so we also need to verify that a persisted refresh token exists for the user in Firestore (created during account linking).</p><pre>const { smarthome } = require(&#39;actions-on-google&#39;);<br><strong>const homegraph = smarthome({<br>  jwt: require(&#39;./service-account.json&#39;)<br>});</strong></pre><pre>/**<br> * Cloud Function: Request a sync with the Assistant HomeGraph<br> * on device add<br> */<br>functions.firestore.document(&#39;devices/{device}&#39;).onCreate(<br>  async (snapshot, context) =&gt; {<br>    // Obtain the device owner UID<br>    const userId = snapshot.data().owner;<br>    const linked = await verifyAccountLink(userId);<br>    if (linked) {<br>      <strong>await homegraph.requestSync(userId);</strong><br>    }<br>  });</pre><pre>/**<br> * Cloud Function: Request a sync with the Assistant HomeGraph<br> * on device remove<br> */<br>functions.firestore.document(&#39;devices/{device}&#39;).onDelete(<br>  async (snapshot, context) =&gt; {<br>    // Obtain the device owner UID<br>    const userId = snapshot.data().owner;<br>    const linked = await verifyAccountLink(userId);<br>    if (linked) {<br>      <strong>await homegraph.requestSync(userId);</strong><br>    }<br>  });</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/request-sync.js">functions/smart-home/request-sync.js</a></blockquote><h3>QUERY</h3><p>The <a href="https://developers.google.com/actions/smarthome/create#actiondevicesquery"><strong>QUERY</strong></a> intent asks for the current state of a specific set of devices (noted by their ids). A <strong>QUERY</strong> may be sent by the Google Assistant in response to a voice command (e.g. “What is the current temperature in the hallway?”) or to update the UI in the Google Home app.</p><pre>fulfillment.onQuery(async (body, headers) =&gt; {<br>  validateCredentials(headers);<br>  // Return device state for the requested device ids<br>  const deviceSet = {};<br>  for (const target of body.inputs[0].payload.devices) {<br>    <strong>const doc = await firestore.doc(`devices/${target.id}`).get();</strong><br>    const device = new Device(doc.id, doc.data());<br>    deviceSet[device.id] = device.reportState;<br>  }</pre><pre>  return {<br>    requestId: body.requestId,<br>    payload: {<br>      devices: deviceSet<br>    }<br>  };<br>});</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><p>The device cloud sample project stores this data in the state field for each device using an internal representation of the device attributes. Our <strong>QUERY</strong> handler converts these attributes to match the device state values required by the Assistant for each trait. Our light bulb and thermostat devices declared support for the following traits:</p><ul><li><a href="https://developers.google.com/actions/smarthome/traits/onoff#device-states">OnOff</a></li><li><a href="https://developers.google.com/actions/smarthome/traits/brightness#device-states">Brightness</a></li><li><a href="https://developers.google.com/actions/smarthome/traits/temperaturesetting#device-states">TemperatureSetting</a></li></ul><p>Below is an example of the device entries in the <strong>QUERY</strong> response returning the state for each supported trait:</p><pre>{<br>  &#39;light-123abc&#39;: {<br>    online: true,<br>    on: true,<br>    brightness: 100<br>  },<br>  &#39;thermostat-123abc&#39;: {<br>    online: true,<br>    thermostatMode: &#39;heat&#39;,<br>    thermostatTemperatureSetpoint: &#39;20&#39;,<br>    thermostatTemperatureAmbient: &#39;17&#39;<br>  }<br>}</pre><h3>EXECUTE</h3><p>When the user issues a command (e.g. “Turn on the kitchen light”), your service receives an <a href="https://developers.google.com/actions/smarthome/create#actiondevicesexecute"><strong>EXECUTE</strong></a> intent. This intent provides a distinct set of traits to be updated for a given set of device ids. This allows a single intent to update a group of traits or devices simultaneously.</p><p>Here, we update the contents of the device-configs document for each device, which triggers Cloud IoT Core to publish the configuration change. As we discussed in the previous post, the device will report its new state to Firestore in the devices collection after the change is processed successfully.</p><pre>fulfillment.onExecute(async (body, headers) =&gt; {<br>  validateCredentials(headers);<br>  // Update the device configs for each requested id<br>  const command = body.inputs[0].payload.commands[0];<br>  <br>  // Apply the state update to each device<br>  const update = Device.stateFromExecution(command.execution);<br>  const batch = firestore.batch();<br>  for (const target of command.devices) {<br><strong>    const configRef = firestore.doc(`device-configs/${target.id}`);<br>    batch.update(configRef, update);</strong><br>  }<br>  await batch.commit();</pre><pre>  return {<br>    requestId: body.requestId,<br>    payload: {<br>      commands: {<br>        ids: command.devices.map(device =&gt; device.id),<br><strong>        status: &#39;PENDING&#39;</strong><br>      }<br>    }<br>  };<br>});</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><p>The <strong>EXECUTE</strong> response must return a status code indicating whether each command was successful in changing the device state. If the cloud service can synchronously verify that the command reached the device and updated it, then it returns a <strong>SUCCESS</strong>. If the device is unreachable, then the service can report <strong>OFFLINE</strong> or <strong>ERROR</strong>.</p><p>Since our commands are written to Firestore through one document and the result is sent asynchronously back through another, we report <strong>PENDING</strong> rather than reporting <strong>SUCCESS</strong>. This indicates that we expect the command to succeed, and we will report the state change when it arrives.</p><h3>Report State</h3><p>Integrate the <a href="https://developers.google.com/actions/smarthome/report-state">Report State</a> API into your service to proactively report changes in device state to the Google Assistant. This is necessary to publish the latest device information to the Home Graph, which enables Google to look up device state without sending additional QUERY intents to your service.</p><p>In our example, we can define a new cloud function that triggers on updates to the devices collection. Recall from the previous post that this is where state updates from Cloud IoT Core are published. The function takes the updated device state and forwards it to the Home Graph API.</p><pre>const { smarthome } = require(&#39;actions-on-google&#39;);<br>const homegraph = smarthome({<br>  jwt: require(&#39;./service-account.json&#39;)<br>});</pre><pre>/**<br> * Cloud Function: Report device state changes to<br> * Assistant HomeGraph<br> */<br><strong>functions.firestore.document(&#39;devices/{device}&#39;).onUpdate(</strong><br>  async (change, context) =&gt; {<br>    const deviceId = context.params.device;<br>    const device = new Device(deviceId, change.after.data());</pre><pre>    // Check if user has linked to Assistant<br>    const linked = await verifyAccountLink(device.owner);<br>    if (linked) {<br>      // Send a state report<br>      const report = {};<br>      report[`${device.id}`] = device.reportState;<br>      <strong>await homegraph.reportState({<br>        requestId: uuid(),<br>        agentUserId: device.owner,<br>        payload: {<br>          devices: {<br>            states: report<br>          }<br>        }<br>      });</strong><br>    }<br>  });</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/report-state.js">functions/smart-home/report-state.js</a></blockquote><blockquote>The format of the states reported for each device is the same as a <strong>QUERY</strong> response.</blockquote><h3>DISCONNECT</h3><p>Your service receives a <a href="https://developers.google.com/actions/smarthome/create#actiondevicesdisconnect"><strong>DISCONNECT</strong></a> intent if the user decides to unlink their account from the Google Assistant. The service should invalidate the credentials used to provide access to this user’s devices.</p><p>For our example, this means clearing out the stored refresh token we generated during the account linking process. This negates any future attempts to gain a new access token until the user links their account again.</p><pre>fulfillment.onDisconnect(async (body, headers) =&gt; {<br>  const userId = validateCredentials(headers);</pre><pre>  // Clear the user&#39;s current refresh token<br><strong>  const userRef = firestore.doc(`users/${userId}`);<br>  await userRef.delete();</strong></pre><pre>  // Return empty body<br>  return {};<br>});</pre><blockquote>Snippet from <a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud/blob/master/firebase/functions/smart-home/fulfillment.js">functions/smart-home/fulfillment.js</a></blockquote><h3>What’s next?</h3><p>Congratulations! Now your user’s devices are accessible through the Google Assistant. Check out the following resources to go deeper and learn more about building smart home Actions for the Google Assistant:</p><ul><li><a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud">Smart Home Device Manager sample</a></li><li><a href="https://developers.google.com/actions/smarthome/">Smart Home Actions documentation</a></li><li><a href="https://actions-on-google.github.io/actions-on-google-nodejs/">Actions on Google client library</a></li></ul><p>You can also follow <a href="https://twitter.com/ActionsOnGoogle">@ActionsOnGoogle</a> on Twitter and connect with other smart home developers in our <a href="https://www.reddit.com/r/GoogleAssistantDev">Reddit community</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3901ab39c39c" width="1" height="1"><hr><p><a href="https://medium.com/google-developers/smart-home-cloud-services-with-google-part-2-3901ab39c39c">Smart Home Cloud Services with Google: Part 2</a> was originally published in <a href="https://medium.com/google-developers">Google Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Smart Home Cloud Service with Google]]></title>
            <link>https://medium.com/google-developers/building-a-smart-home-cloud-service-with-google-1ee436ac5a03?source=rss-c91ac3b63127------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ee436ac5a03</guid>
            <category><![CDATA[actions-on-google]]></category>
            <category><![CDATA[smart-home]]></category>
            <category><![CDATA[internet-of-things]]></category>
            <category><![CDATA[google-assistant]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Dave Smith]]></dc:creator>
            <pubDate>Mon, 04 Mar 2019 14:30:36 GMT</pubDate>
            <atom:updated>2019-03-18T19:29:19.223Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently, my colleague Dan Myers wrote a great piece on <a href="https://medium.com/google-developers/iot-google-assistant-f0908f354681">IoT &amp; Google Assistant</a>, introducing the core concepts of the Smart Home Actions API. This API enables developers to report device state to the Home Graph from their existing cloud service infrastructure and execute commands sent from Assistant-enabled devices.</p><p>In the article, Dan mentions that in order to integrate your devices with Google Assistant, <em>“you, as the device creator, develop your own cloud service, which includes its own dashboard, device registration, and device management that functions independently of the Assistant.”</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*uwXDSz-8mjLJq0-S" /><figcaption>Figure 1: Assistant Communication Flow</figcaption></figure><p>In this post, I’d like to explore what that cloud service might look like if you’re a developer who hasn’t already invested the time and resources into building your own device cloud — or simply don’t want to manage the cloud infrastructure yourself as your device fleet scales. Maybe you’re just looking into getting your existing products connected, and wondering what it takes to build a cloud service for the smart home.</p><p>We’re going to discuss a working example of a smart home cloud using components of Firebase and the Google Cloud Platform. You will see how to securely connect and provision devices, build a user-authenticated device model that enables easy front-end user application development for both mobile and the web, and set up the structure necessary to integrate with the Google Assistant.</p><blockquote><em>You can find the sample code described in this post on </em><a href="https://github.com/GoogleCloudPlatform/iot-smart-home-cloud"><em>GitHub</em></a><em>.</em></blockquote><h3>Inside the blue box</h3><p>Expanding a bit on what a smart home device cloud should look like, here are some typical requirements you might have:</p><ul><li>Send commands to devices from the cloud</li><li>Report state from devices to the cloud</li><li>Detect when devices are offline</li><li>Provision devices to individual users</li><li>Provide users access to manage their devices</li></ul><p>Not to mention that you want all of this to happen securely as data travels between users, the cloud, and devices. We can satisfy all of these requirements using services from the Google Cloud Platform and Firebase to create a scalable serverless solution for home devices.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*kWObeRg45sziM44j" /><figcaption>Figure 2: Cloud Service Architecture</figcaption></figure><p><a href="https://cloud.google.com/iot/docs/">Cloud IoT Core</a> is a fully managed service for securely connecting and managing IoT devices. Using the MQTT or HTTP bridge, IoT devices can connect to Google Cloud using per-device public/private key authentication and exchange data. Incoming device data is published to a <a href="https://cloud.google.com/pubsub/">Cloud Pub/Sub</a> event stream.</p><p><a href="https://firebase.google.com/docs/firestore/">Cloud Firestore</a> is a flexible, scalable NoSQL cloud database to store and sync data for client- and server-side development. It keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web through their native SDKs. Firestore also pairs with <a href="https://firebase.google.com/docs/auth/">Firebase Authentication</a> to control access to data through built-in security rules.</p><p><a href="https://firebase.google.com/docs/functions/">Cloud Functions</a> enable backend code to run in response to events triggered by Firebase and Google Cloud features. We can use this to marshal device data between IoT Core and Firestore. Using this architecture, Cloud Firestore becomes the central data hub and source of truth for the state of all connected devices and exposes that state to authenticated client applications.</p><h3>Device Model</h3><p>The <a href="https://firebase.google.com/docs/firestore/data-model">Firestore model</a> represents data as key-value pairs stored in <em>documents</em>, which are then organized into <em>collections</em>. We will represent each smart home device in Firestore using two documents:</p><ol><li>Document within a <strong>devices</strong> collection containing the device metadata, online status, and latest state reported by the device.</li><li>Document within a <strong>device-configs</strong> collection with the user’s latest requested state (e.g. “set the thermostat to 75 degrees”).</li></ol><pre><strong>devices/light-123abc</strong><br>  name: &quot;Kitchen Light&quot;<br>  owner: <em>$user-id</em><br>  type: &quot;light&quot;<br>  online: true<br>  state: {<br>    on: true<br>    brightness: 100<br>  }</pre><pre><strong>device-configs/light-123abc</strong><br>  owner: <em>$user-id</em><br>  value: {<br>    on: true<br>    brightness: 100<br>  }</pre><pre><strong>devices/thermostat-123abc</strong><br>  name: &quot;Hallway Thermostat&quot;<br>  owner: <em>$user-id</em><br>  type: &quot;thermostat&quot;<br>  online: true<br>  state: {<br>    mode: heat<br>    current: 70<br>    setpoint: 72<br>  }</pre><pre><strong>device-configs/thermostat-123abc</strong><br>  owner: <em>$user-id</em><br>  value: {<br>    on: true<br>    setpoint: 72<br>  }</pre><p>Changes to the device’s config document trigger a cloud function to update the device. By separating the configuration and state into two documents, we ensure that successful updates received from the device don’t trigger the same logic. This simplifies the function logic and creates a nice separation outgoing and incoming data flows.</p><h3>Device Communication</h3><p>We need to map changes in state to messages that we can exchange with the device. Cloud IoT Core supports the following message types:</p><ul><li><strong>Configuration:</strong> Sent from the cloud to the device, up to one message per second. Configuration messages are guaranteed to be delivered to the device.</li><li><strong>Commands:</strong> Send up to 100 messages per second from the cloud to the device. Commands are only delivered if the device is online.</li><li><strong>State:</strong> Sent from the device to the cloud, up to one message per second. State updates are delivered to active Pub/Sub subscribers and recent updates are persisted inside Cloud IoT Core.</li><li><strong>Telemetry:</strong> Send up to 100 messages per second from the device to the cloud. Telemetry events are only delivered to active Pub/Sub subscribers.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/680/0*LTjHCIXWTD83FYf_" /><figcaption>Figure 3: Cloud IoT Message Types</figcaption></figure><p>We want user commands to turn on the lights or adjust the temperature to be guaranteed to arrive at the device, even if that device happens to be offline. With configuration messages, this is handled for us by IoT Core. The device will receive the latest configuration each time it connects to the gateway, removing the need for our application logic to handle this.</p><p>Using cloud functions, we can deploy code that triggers when a device config document changes in Firestore, and publish those to the corresponding device in IoT Core. The document path used as a trigger contains a wildcard for the device id. This allows the function to trigger for every device, and captures the device id value in the function’s context.</p><pre><strong>functions.firestore.document(&#39;device-configs/{device}&#39;)</strong>.onWrite(<br>  async (change, context) =&gt; {<br>    const deviceId = context.params.device;<br>    const config = change.after.data();</pre><pre>    ...</pre><pre>    // Create a new Cloud IoT client<br>    const client = google.cloudiot({<br>      version: &#39;v1&#39;,<br>      auth: auth<br>    });</pre><pre>    // Update IoT Core configuration<br>    const parent =<br>      &#39;projects/my-project/locations/us-central1&#39;;<br>    const devicePath = `${parent}/registries/my-registry`;<br>    const request = {<br>      name: `${devicePath}/devices/${deviceId}`,<br>      binaryData: Buffer.from(JSON.stringify(config))<br>        .toString(&quot;base64&quot;),<br>    };<br>    <strong>await client.projects.locations.registries.devices<br>      .modifyCloudToDeviceConfig(request);</strong><br>  });</pre><p>When the device reports its state back to IoT Core, the message gets published to a Cloud Pub/Sub topic that we choose. We can then use another function to capture those messages and write the updated state into Firestore.</p><pre><strong>functions.pubsub.topic(&#39;device-events&#39;)</strong>.onPublish(<br>  async (message) =&gt; {<br>    const deviceId = message.attributes.deviceId;</pre><pre>    // Write the device state into Firestore<br>    const deviceRef = firestore.doc(`devices/${deviceId}`);<br>    try {<br>      <strong>await deviceRef.update({<br>        &#39;state&#39;: message.json,<br>        &#39;online&#39;: true<br>      });</strong><br>      console.log(`State updated for ${deviceId}`);<br>    } catch (error) {<br>      console.error(error);<br>    }<br>});</pre><p>Our devices could publish this data as either state updates or telemetry events, and the code would behave the same way. However, because Firestore does the work of persisting device state in our cloud service, we don’t need IoT Core to do the same. Therefore, it makes the most sense to have the device send data using telemetry events.</p><blockquote><strong><em>NOTE:</em></strong><em> The </em><strong><em>sample-device</em></strong><em> project on GitHub implements a virtual device using Node.js that connects to the MQTT gateway, subscribes to configuration changes, and publishes updates back as telemetry events.</em></blockquote><h3>Building the Clients</h3><p>Because all of the device data resides in Firestore, building user applications for the web and mobile devices is very straightforward using the native Firebase SDKs. To make things even simpler, the Firebase team provides <a href="https://firebase.google.com/docs/libraries/">additional libraries</a> that integrate these SDKs with popular frameworks.</p><p>This example uses <a href="https://angular.io/">Angular</a> and <a href="https://github.com/angular/angularfire2">AngularFire</a> for the web interface. The mobile application was built in <a href="https://flutter.io/">Flutter</a> with <a href="https://github.com/flutter/plugins/blob/master/FlutterFire.md">FlutterFire</a>, which has the added benefit of being cross-platform between iOS and Android devices.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/649/1*4QMXtpoJVgUiO-TX-eR5gQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SB7JZX_swZQ0cY_2" /><figcaption>Figure 4: Client Applications</figcaption></figure><p>Security rules define access control for the data housed in Firestore, ensuring only authorized users have permission to view and manage the devices they own. The rule set below defines the following controls:</p><ol><li>Users can read, change, or remove a device where they are listed as the owner</li><li>Users cannot create a new device entry (this will be handled by device provisioning)</li><li>Users can create and modify a pending device entry (part of device provisioning)</li></ol><pre>service cloud.firestore {<br>  match /databases/{database}/documents {<br>    match /devices/{deviceid} {<br>      allow read, update, delete:<br>       if request.auth.uid == resource.data.owner;<br>    }<br>    <br>    match /device-configs/{deviceid} {<br>      allow read, update, delete:<br>       if request.auth.uid == resource.data.owner;<br>    }<br>    <br>    match /pending/{deviceid} {<br>      allow read, write:<br>        if request.auth.uid == resource.data.owner;<br>      allow create:<br>        if request.auth.uid != null;<br>    }<br>  }<br>}</pre><p>Since Firestore represents the source of truth for device data, client applications only need to interface with the Firebase SDK to authenticate users and manage devices. Users can view their devices by listing the documents in the <strong>devices</strong> collection where their account matches the <strong>owner</strong> field. The following Dart code lists the documents for the current user’s devices using the FlutterFire plugin.</p><pre>Firestore.instance.collection(&#39;devices&#39;)<br>  .where(&#39;owner&#39;, isEqualTo: user.uid)<br>  .snapshots()</pre><p>Each user command to change the state of the device updates the corresponding document in the <strong>device-configs</strong> collection. This triggers an update to the device’s IoT Core configuration using the cloud function described in the previous section.</p><pre>Firestore.instance.collection(&#39;device-configs&#39;)<br>  .document(device.id)<br>  .updateData({<br>    &#39;value&#39;: ...<br>  });</pre><p>Registering a new device to the user’s account requires a few additional steps for security purposes, so let’s look in more detail at that process.</p><h3>Device Provisioning</h3><p>Cloud IoT Core uses public/private key pairs to authenticate devices. We won’t discuss the process in detail here, but see <a href="https://cloud.google.com/iot/docs/concepts/device-security">device security</a> for more information on how it works. For consumer devices, we will split the provisioning process into two stages: factory provisioning and end-user provisioning.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*ngNK3tpnqMlPPI-q" /><figcaption>Figure 5: Device Provisioning Process</figcaption></figure><p>The factory assigns a private key to the physical device, and registers the corresponding public key and device identifier with Cloud IoT Core. By doing this at the factory, we ensure only devices we manufacture have credentials to access the cloud. The device is shipped to the end user with a copy of the public key, which the user application must provide in order to register that device to their account.</p><p>In our example, the public key and device metadata are bound to a QR code that the user can scan from their mobile device during the device registration process. The example QR code contains the following information as a JSON blob:</p><pre>{<br>  &quot;type&quot;:&quot;light&quot;,<br>  &quot;serial_number&quot;:&quot;abcdef123456&quot;,<br>  &quot;public_key&quot;:&quot;MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4InTLsvDq9Km...&quot;<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*JwF91So7p7x56EVU" /><figcaption>Figure 6: Example Device Registration Code</figcaption></figure><p>The user application writes this data into Firestore as a pending device registration, which triggers a cloud function to verify that the public key from the physical device matches the value provisioned in Cloud IoT Core for the corresponding device identifier and that the device is not already registered to a different account.</p><pre>functions.firestore.document(&#39;pending/{device}&#39;).onWrite(<br>  async (change, context) =&gt; {<br>    const deviceId = context.params.device;<br>    const pending = change.after.data();<br>    <br>    ...</pre><pre>    // Create a new Cloud IoT client<br>    const client = google.cloudiot({<br>      version: &#39;v1&#39;,<br>      auth: auth<br>    });<br>    const parent = &#39;projects/my-project/locations/us-central1&#39;;<br>    const devicePath = `${parent}/registries/my-registry`;<br>    const request = {<br>      name: `${devicePath}/devices/${deviceId}`<br>    }</pre><pre>    try {<br><strong>      // Verify device does NOT already exist in Firestore</strong><br>      const deviceRef = firestore.doc(`devices/${deviceId}`);<br>      const deviceDoc = await deviceRef.get();<br>      if (deviceDoc.exists)<br>        throw `${deviceId} is already registered to another user`;</pre><pre><strong>      // Verify device exists in IoT Core</strong><br>      // Throws an error if device id does not exist<br>      const result = await client.projects.locations.registries<br>        .devices.get(request);</pre><pre><strong>      // Verify the device public key matches</strong><br>      const deviceKey = result.credentials[0].publicKey.key<br>        .trim();<br>      const pendingKey = pending.public_key;<br>      if (deviceKey !== pendingKey) throw &#39;Public Key Mismatch&#39;;</pre><pre>      ...</pre><pre>    } catch (error) {<br>      console.error(error);<br>    }<br>  });</pre><p>Once the device data has been verified, the server can create the new device entry for the user and remove the pending entry from Firestore.</p><h3>What’s Next?</h3><p>In this post, we’ve explored what it takes to get started building a cloud service for smart home devices using the Google Cloud Platform and Firebase, and how these services make developing front-end applications easy while keeping everything secure end-to-end.</p><p>In the next post of this series, you will see how the groundwork we have laid enables us to quickly add the account linking and intent fulfillment necessary to connect your new device cloud with <a href="https://developers.google.com/actions/smarthome/">Smart Home Actions</a> and the Google Assistant.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ee436ac5a03" width="1" height="1"><hr><p><a href="https://medium.com/google-developers/building-a-smart-home-cloud-service-with-google-1ee436ac5a03">Building a Smart Home Cloud Service with Google</a> was originally published in <a href="https://medium.com/google-developers">Google Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Computing at the Edge of IoT]]></title>
            <link>https://medium.com/google-developers/computing-at-the-edge-of-iot-140a888007bd?source=rss-c91ac3b63127------2</link>
            <guid isPermaLink="false">https://medium.com/p/140a888007bd</guid>
            <category><![CDATA[android-things]]></category>
            <category><![CDATA[internet-of-things]]></category>
            <category><![CDATA[edge-computing]]></category>
            <dc:creator><![CDATA[Dave Smith]]></dc:creator>
            <pubDate>Fri, 09 Feb 2018 15:01:02 GMT</pubDate>
            <atom:updated>2018-02-09T15:01:02.289Z</atom:updated>
            <content:encoded><![CDATA[<p>Over the past year, we’ve had some great conversations with developers about building IoT devices with the <a href="https://developer.android.com/things/index.html">Android Things</a> platform. A common question that comes up is whether the platform is suitable for the Internet of Things (IoT) given that the hardware is much more powerful than the microcontrollers typically found in the space today. To answer that question, let’s examine how hardware choice and use case requirements factor into different IoT system architectures.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rO-hFHYwCS79ldLj." /><figcaption>“Computer programmer&#39;s single microchip” by <a href="https://unsplash.com/@briankost?utm_source=medium&amp;utm_medium=referral">Brian Kostiuk</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>You already lost me, what’s a microcontroller?</h3><p><a href="https://en.wikipedia.org/wiki/Microcontroller">Microcontrollers</a> (MCUs) are simple, programmable, and fully integrated systems typically used for embedded control. In addition to a processor (CPU), they generally include all of the memory and peripheral interfaces necessary on a single chip. This simplicity and integration means that MCUs are relatively inexpensive and generally consume very little power. Many popular hardware development platforms, such as <a href="https://www.arduino.cc/">Arduino</a>, are built on top of MCUs.</p><p>MCUs generally do not have the resources (such as a <a href="https://en.wikipedia.org/wiki/Memory_management_unit">Memory Management Unit</a>) to run a higher-level operating system like Linux or Android. Nor can they interface with high-speed peripherals like high-resolution cameras and displays. However, because the application code runs much closer “to the metal”, MCUs are very effective in real-time applications where timing is critical. Production MCUs often run some flavor of a <a href="https://en.wikipedia.org/wiki/Real-time_operating_system">real-time operating system</a> (RTOS) to ensure tasks run in the exact amount of time required to ensure precise measurement and control.</p><p>All of these characteristics start to define applications where MCUs are a perfect fit…and where they aren’t.</p><h3>The race to the cloud</h3><p>Systems focused primarily (or entirely) on MCU hardware are based on what I’ll call the <strong><em>cloud first architecture</em></strong>. In this architecture, every edge device is connected directly to the internet (usually through WiFi), then provisioned and managed through a cloud service such as Google’s <a href="https://cloud.google.com/iot-core/">Cloud IoT Core</a>. Inexpensive MCU platforms with built-in WiFi stacks, like the popular <a href="http://espressif.com/en/products/hardware/esp8266ex/overview">ESP8266</a>, make designing systems like this very attractive.</p><p>In these systems, complex data analysis and decision making tasks are handled in the cloud back-end, while the device nodes perform data collection tasks or respond to remote control commands.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/596/1*Yn-5S9ZU8xdxgVZBtKrNAg.png" /><figcaption>Cloud first architecture</figcaption></figure><p>Overall, this is a nice balance. Hardware is inexpensive to replace and can run on small batteries for multiple years, and heavy compute resources are provided by cloud services that are easy to scale up to meet demand as the number of edge devices increases.</p><p>MCU hardware and a cloud-first architecture perform well in applications where bandwidth and network latency are less of a concern. Data payloads are small and can be uploaded to the cloud in batches. Here are some examples:</p><ul><li>Distributed sensor-based data collection</li><li>Mobile asset monitoring and tracking</li></ul><h3>Living on the edge</h3><p>There is a trend in IoT systems moving towards <a href="https://en.wikipedia.org/wiki/Edge_computing">edge computing</a>, enabled by the smartphone economy driving down the cost (and power consumption) of more capable hardware. There are four main reasons why applications perform computing tasks at the edge:</p><ol><li><strong>Privacy:</strong> Avoid sending all raw data to be stored and processed on cloud servers.</li><li><strong>Bandwidth:</strong> Reduce costs associated with transmitting all raw data to cloud services.</li><li><strong>Latency:</strong> Reaction time is critical and cannot be dependent on a cloud connection.</li><li><strong>Reliability:</strong> The ability to operate even when the cloud connection is interrupted.</li></ol><p>A great example of this in practice is the Google Home device. While the Google Assistant functionality is cloud-driven, the hotword detection happens locally on the device. This protects user privacy by avoiding uploads of audio data until the device knows you are talking to it, but it also eliminates the bandwidth that would have been consumed uploading raw audio to the cloud. Without edge computing, a device like this would not be feasible for consumer use.</p><p>This is also true in industrial automation systems where latency and reliability are critical. In these systems, one or more intermediate gateway devices act as an interface between local edge devices (which may be MCU-powered) and any cloud services.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/666/1*Kk6uThxWayx1M4kkaf2kTQ.png" /><figcaption>Edge computing pushes intelligence out of the central cloud</figcaption></figure><p>Devices running Android Things are well-suited to gateway applications because they have the computational horsepower to locally apply data transformations and automation rules, paired with the SDK support to easily integrate with the <a href="https://cloud.google.com/products/">Google Cloud Platform</a> APIs.</p><p>There is also an upside here for MCU-powered nodes. In areas where the lowest power consumption is still critical, WiFi and Ethernet can often strain (or break) the power budget. If we remove the need to connect each device to a cloud service, we can substitute a more power-efficient local network transport, such as Bluetooth Low Energy or an 802.15.4 radio attached to a <a href="https://www.threadgroup.org/What-is-Thread/Overview">Thread</a> mesh network.</p><h3>Making IoT smarter</h3><p>The advancements in both artificial intelligence (AI) and machine learning (ML) promise to have big effects on IoT systems in the coming years. The ability for ML algorithms to find patterns and make predictions based on data collected by devices will quickly become a necessary component to the success of IoT as the number of devices (and therefore the volume of data) continues to grow.</p><p>Systems built around a cloud-first architecture can already take advantage of services in <a href="https://cloud.google.com/products/machine-learning/">Google’s Cloud AI</a> suite, such as <a href="https://cloud.google.com/vision/">Cloud Vision</a> and <a href="https://cloud.google.com/speech/">Cloud Speech</a>, to enable machine learning. MCUs can interface with these services through REST APIs or indirectly via cloud functions and the bridges provided by Cloud IoT Core. The “heavy lifting”, however, must still be done in the cloud.</p><p>To truly scale these types of capabilities in IoT systems, we need to decentralize and push as much AI/ML as possible to the edge. This is where a platform like Android Things starts to really shine. In addition to having the necessary compute power, client libraries like the <a href="https://developers.google.com/assistant/sdk/">Google Assistant SDK</a> and <a href="https://www.tensorflow.org/mobile/tflite/">TensorFlow Lite</a> enable edge devices to perform these complex tasks with very little developer integration work.</p><h3>What’s next?</h3><p>As I mentioned earlier, one of the advantages of MCUs is the ability to essentially control what happens on every clock cycle, providing real-time control over the I/O on the chip. This enables application firmware to implement custom protocols not directly supported by the hardware controllers on the processor.</p><p>We are starting to see chip vendors build these capabilities into their hardware, including both application processor (Cortex-A) and MCU (Cortex-M) cores within the same package. Architectures like this will enable developers to have a dedicated real-time core within the larger system, essentially providing the best of both worlds.</p><h3>The right tools for the job</h3><p>We’ve seen that demand for low latency, offline access, and enhanced machine learning capabilities is fueling a move towards decentralization with more powerful computing devices at the edge. Nevertheless, many distributed applications benefit more from a centralized architecture and the lowest cost hardware powered by MCUs. It all comes down to evaluating what your system’s needs truly are, and selecting the right tools for the job.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=140a888007bd" width="1" height="1"><hr><p><a href="https://medium.com/google-developers/computing-at-the-edge-of-iot-140a888007bd">Computing at the Edge of IoT</a> was originally published in <a href="https://medium.com/google-developers">Google Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[This may not seem necessary at first, since you can get away with just calling forceLoad() in here…]]></title>
            <link>https://medium.com/@devunwired/this-may-not-seem-necessary-at-first-since-you-can-get-away-with-just-calling-forceload-in-here-b1870080ba22?source=rss-c91ac3b63127------2</link>
            <guid isPermaLink="false">https://medium.com/p/b1870080ba22</guid>
            <dc:creator><![CDATA[Dave Smith]]></dc:creator>
            <pubDate>Thu, 30 Jun 2016 21:35:43 GMT</pubDate>
            <atom:updated>2016-06-30T21:35:43.492Z</atom:updated>
            <content:encoded><![CDATA[<p>This may not seem necessary at first, since you can get away with just calling forceLoad() in here for and it works. During configuration changes, the LoaderManager caches the data to this cache isn’t actually used.</p><p>But there is one case that this local cache makes sense—a paused activity. If an activity is paused and then resumed, the default behavior will fetch again unless you serve a local copy like this one.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b1870080ba22" width="1" height="1">]]></content:encoded>
        </item>
    </channel>
</rss>