Android पर ऑटोमेशन बनाना

ऑटोमेशन एपीआई को Android के लिए Home API की मदद से ऐक्सेस किया जा सकता है. हालांकि, इनका एंट्री पॉइंट स्ट्रक्चर के ज़रिए होता है. इसलिए, इनका इस्तेमाल करने से पहले स्ट्रक्चर पर अनुमति देना ज़रूरी है.

किसी स्ट्रक्चर के लिए अनुमतियां मिलने के बाद, इन पैकेज को अपने ऐप्लिकेशन में इंपोर्ट करें:


import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure

स्ट्रक्चर में एक HasAutomations इंटरफ़ेस होता है. इसमें ऑटोमेशन के लिए ये तरीके शामिल होते हैं:

एपीआई जानकारी
automations() स्ट्रक्चर से जुड़े सभी ऑटोमेशन की सूची. सिर्फ़ वे ऑटोमेशन दिखाए जाते हैं जिन्हें आपने Home API की मदद से बनाया है.
createAutomation(automation) किसी स्ट्रक्चर के लिए ऑटोमेशन इंस्टेंस बनाएं.
deleteAutomation(automationId) किसी ऑटोमेशन इंस्टेंस को उसके आईडी से मिटाएं.

कोई ऑटोमेशन बनाना

होम का इंस्टेंस बनाने और उपयोगकर्ता से अनुमतियां मिलने के बाद, स्ट्रक्चर और डिवाइसों की जानकारी पाएं:

val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!

इसके बाद, ऑटोमेशन डीएसएल का इस्तेमाल करके, अपने ऑटोमेशन का लॉजिक तय करें. Home API में, ऑटोमेशन को Automation इंटरफ़ेस से दिखाया जाता है. इस इंटरफ़ेस में प्रॉपर्टी का एक सेट होता है:

  • मेटाडेटा, जैसे कि नाम और ब्यौरा.
  • फ़्लैग से पता चलता है कि ऑटोमेशन को चलाया जा सकता है या नहीं.
  • नोड की एक सूची जिसमें ऑटोमेशन का लॉजिक होता है. इसे ऑटोमेशन ग्राफ़ कहा जाता है. इसे automationGraph प्रॉपर्टी से दिखाया जाता है.

automationGraph, डिफ़ॉल्ट रूप से SequentialFlow टाइप का होता है. यह एक क्लास है, जिसमें क्रम से चलने वाले नोड की सूची होती है. हर नोड, ऑटोमेशन के किसी एलिमेंट को दिखाता है. जैसे, कोई स्टार्टर, शर्त या कार्रवाई.

ऑटोमेशन को name और description असाइन करें.

ऑटोमेशन बनाने पर, isActive फ़्लैग डिफ़ॉल्ट रूप से true पर सेट हो जाता है. इसलिए, इस फ़्लैग को साफ़ तौर पर सेट करना ज़रूरी नहीं है. हालांकि, अगर आपको शुरुआत में ऑटोमेशन को बंद करना है, तो इसे सेट करना ज़रूरी है. ऐसे में, कैंपेन बनाते समय फ़्लैग को false पर सेट करें.

DraftAutomation इंटरफ़ेस का इस्तेमाल, ऑटोमेशन बनाने के लिए किया जाता है. वहीं, Automation इंटरफ़ेस का इस्तेमाल, जानकारी को वापस पाने के लिए किया जाता है. उदाहरण के लिए, यहां एक ऑटोमेशन के लिए ऑटोमेशन डीएसएल दिया गया है. यह ऑटोमेशन, एक डिवाइस के चालू होने पर दूसरे डिवाइस को चालू करता है:

import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Condition
import com.google.home.automation.DraftAutomation
import com.google.home.automation.Equals
import com.google.home.automation.Node
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.matter.standard.OnOff
import com.google.home.Structure

...

val automation: DraftAutomation = automation {
  name = "MyFirstAutomation"
  description = "Turn on a device when another device is turned on."
  sequential {
    val starterNode = starter<_>(device1, OnOffLightDevice, trait=OnOff)
    condition() { expression = stateReaderNode.onOff equals true }
    action(device2, OnOffLightDevice) { command(OnOff.on()) }
  }
}

ऑटोमेशन डीएसएल तय करने के बाद, DraftAutomation इंस्टेंस बनाने के लिए, इसे createAutomation() तरीके में पास करें:

val createdAutomation = structure.createAutomation(automation)

यहां से, ऑटोमेशन के लिए अन्य सभी तरीकों का इस्तेमाल किया जा सकता है. जैसे, execute(), stop(), और update().

पुष्टि से जुड़ी गड़बड़ियां

अगर ऑटोमेशन बनाने की प्रोसेस की पुष्टि नहीं हो पाती है, तो चेतावनी या गड़बड़ी के मैसेज से समस्या के बारे में जानकारी मिलती है. ज़्यादा जानकारी के लिए, ValidationIssueType रेफ़रंस देखें.

अगर createAutomation() बिना किसी गड़बड़ी के पूरा हो जाता है, तो भी हो सकता है कि बनाया गया ऑटोमेशन मान्य न हो या उसे लागू न किया जा सके. बैकएंड, अमान्य ऑटोमेशन ड्राफ़्ट को सेव करने की अनुमति देता है. उदाहरण के लिए, अगर किसी उपयोगकर्ता ने जाने-पहचाने चेहरों की पहचान करने की सुविधा जैसी ज़रूरी अनुमतियां नहीं दी हैं या डिवाइस में ज़रूरी सुविधाएं मौजूद नहीं हैं.

आपको हमेशा isValid की पुष्टि करनी चाहिए और वापस किए गए Automation इंस्टेंस पर validationIssues की जांच करनी चाहिए:

val createdAutomation = structure.createAutomation(automation)

if (!createdAutomation.isValid) {
  // Iterate through validation issues to identify errors and warnings
  for (issue in createdAutomation.validationIssues) {
    when (issue.severity) {
      ValidationIssueSeverity.ERROR -> {
        Log.e(
          "Automation",
          "Validation error on node ${issue.node}: ${issue.issueType}"
        )
        // Handle error (for example, prompt the user to enable missing
        // consents or device features)
      }
      ValidationIssueSeverity.WARNING -> {
        Log.w(
          "Automation",
          "Validation warning on node ${issue.node}: ${issue.issueType}"
        )
      }
      else -> {}
    }
  }
}

कोड के उदाहरण

यहां हमने कोड के कुछ उदाहरण दिए हैं. इनका इस्तेमाल, Android पर ऑटोमेशन डिज़ाइन करना पेज पर बताए गए काल्पनिक ऑटोमेशन के कुछ हिस्सों को लागू करने के लिए किया जा सकता है.

सिंपल ऑटोमेशन

सुबह 8:00 बजे ब्लाइंड्स को ऊपर करने वाले ऑटोमेशन को इस तरह लागू किया जा सकता है:

// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// determine whether a scheduled automation can be constructed
val isSchedulingSupported =
  allCandidates.any {
    it is EventCandidate &&
      it.eventFactory == Time.ScheduledTimeEvent &&
      it.unsupportedReasons.isEmpty()
  }
// get the blinds present in the structure
val blinds =
  allCandidates
    .filter {
      it is CommandCandidate &&
        it.commandDescriptor == WindowCoveringTrait.UpOrOpenCommand &&
        it.unsupportedReasons.isEmpty()
    }
    .map { it.entity }
    .filterIsInstance<HomeDevice>()
    .filter { it.has(WindowCoveringDevice) }
 if (isSchedulingSupported && blinds.isNotEmpty()) {
  // Proceed to create automation
  val automation: DraftAutomation = automation {
    name = "Day time open blinds"
    description = "Open all blinds at 8AM everyday"
    isActive = true
    sequential {
      // At 8:00am local time....
      val unused =
        starter(structure, Time.ScheduledTimeEvent) {
          parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(8, 0, 0, 0)))
        }
        // ...open all the blinds
       parallel {
        for (blind in blinds) {
          action(blind, WindowCoveringDevice) { command(WindowCovering.upOrOpen()) }
        }
      }
    }
  }
   val createdAutomation = structure.createAutomation(automation)
} else if (!isSchedulingSupported) {
  // Cannot create automation.
  // Set up your address on the structure, then try again.
} else {
  // You don't have any WindowCoveringDevices.
  // Try again after adding some blinds to your structure.
}

जटिल ऑटोमेशन

किसी गतिविधि का पता चलने पर, लाइटों को ब्लिंक करने के लिए ट्रिगर होने वाले ऑटोमेशन को इस तरह लागू किया जा सकता है:

import com.google.home.Home
import com.google.home.HomeClient
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.equals
import com.google.home.automation.parallel
import com.google.home.automation.starter
import com.google.home.google.AssistantBroadcast
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.toggle
import com.google.home.matter.standard.OnOffLightDevice
import java.time.Duration

// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()

// get the lights present in the structure
val availableLights = allCandidates.filter {
   it is CommandCandidate &&
   it.commandDescriptor == OnOffTrait.OnCommand
}.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter {it.has(OnOffLightDevice) ||
         it.has(ColorTemperatureLightDevice) ||
         it.has(DimmableLightDevice) ||
         it.has(ExtendedColorLightDevice)}

val selectedLights = ... // user selects one or more lights from availableLights

automation {
isActive = true

sequential {
   // If the presence state changes...
   val starterNode = starter<_>(structure, AreaPresenceState)
   // ...and if the area is occupied...
   condition() {
      expression = starterNode.presenceState equals PresenceState.PresenceStateOccupied
   }
   // "blink" the light(s)
   parallel {
            for(light in selectedLights) {
            action(light, OnOffLightDevice) { command(OnOff.toggle()) }
            delayFor(Duration.ofSeconds(1))
            action(light, OnOffLightDevice) { command(OnOff.toggle()) }
            delayFor(Duration.ofSeconds(1))
            action(light, OnOffLightDevice) { command(OnOff.toggle()) }
            delayFor(Duration.ofSeconds(1))
            action(light, OnOffLightDevice) { command(OnOff.toggle())}
         }
      }
   }
}

इकाई फ़िल्टर की मदद से, डाइनैमिक तौर पर डिवाइस चुनना

ऑटोमेशन बनाते समय, आपके पास सिर्फ़ कुछ डिवाइसों को चुनने की पाबंदी नहीं होती. एंटिटी फ़िल्टर नाम की सुविधा की मदद से, ऑटोमेशन को रनटाइम के दौरान अलग-अलग शर्तों के आधार पर डिवाइस चुनने की अनुमति मिलती है.

उदाहरण के लिए, इकाई फ़िल्टर का इस्तेमाल करके, ऑटोमेशन की सुविधा इन्हें टारगेट कर सकती है:

  • किसी खास तरह के डिवाइस के सभी डिवाइसों पर
  • किसी खास कमरे में मौजूद सभी डिवाइसों को बंद करना
  • किसी कमरे में मौजूद, एक ही तरह के सभी डिवाइसों को चालू या बंद करना
  • चालू किए गए सभी डिवाइसों पर
  • किसी कमरे में चालू किए गए सभी डिवाइस

इकाई फ़िल्टर इस्तेमाल करने के लिए:

  1. Structure या Room को atExecutionTime() पर कॉल करें. यह TypedExpression<TypedEntity<StructureType>> दिखाता है.
  2. इस ऑब्जेक्ट पर, getDevicesOfType() को कॉल करें और इसे DeviceType पास करें.

एंटिटी फ़िल्टर का इस्तेमाल स्टार्टर, स्टेट रीडर, और कार्रवाइयों में किया जा सकता है.

उदाहरण के लिए, किसी स्टार्टर से ऑटोमेशन को ट्रिगर करने के लिए, लाइट को चालू/बंद करने की सुविधा का इस्तेमाल करना:

// If any light is turned on or off
val starter =
  starter(
    entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )

किसी स्ट्रक्चर में मौजूद सभी लाइटों (खास तौर पर, चालू/बंद की जा सकने वाली लाइटों) की OnOff स्थिति को कैप्चर करने के लिए, स्टेट रीडर में:

// Build a Map<Entity, OnOff>
val onOffStateOfAllLights =
  stateReader(
    entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )

किसी खास कमरे में मौजूद लाइटों की जानकारी पाने और उन्हें किसी शर्त में इस्तेमाल करने के लिए:

val livingRoomLights =
  stateReader(
    entityExpression = livingRoom.atExecutionTime().getDevicesOfType(OnOffLightDevice),
    trait = OnOff,
  )
// Are any of the lights in the living room on?
condition { expression = livingRoomLights.values.any { it.onOff equals true } }

रनटाइम के दौरान:

स्थिति नतीजा
कोई भी डिवाइस, स्टार्टर की ज़रूरी शर्तें पूरी नहीं करता. ऑटोमेशन ट्रिगर नहीं होता.
स्टेट रीडर में कोई भी डिवाइस, ज़रूरी शर्तें पूरी नहीं करता. ऑटोमेशन शुरू हो जाता है, लेकिन यह शर्त वाले नोड के आधार पर जारी रहेगा.
कोई भी डिवाइस, किसी कार्रवाई की ज़रूरी शर्तें पूरी नहीं करता. ऑटोमेशन शुरू हो जाता है, लेकिन कार्रवाई से कुछ नहीं होता.

यहां दिए गए उदाहरण में, एक ऐसा ऑटोमेशन है जो किसी एक लाइट को बंद करने पर, हॉल की लाइट को छोड़कर सभी लाइटें बंद कर देता है:

val unused = automation {
  sequential {
    // If any light is turned on or off
    val starter =
      starter(
        entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice),
        trait = OnOff,
      )
    condition {
      // Check to see if the triggering light was turned off
      expression = starter.onOff equals false
    }
    // Turn off all lights except the hall light
    action(
      entityExpression =
        structure.atExecutionTime().getDevicesOfType(OnOffLightDevice).filter {
          it notEquals entity(hallwayLight, OnOffLightDevice)
        }
    ) {
      command(OnOff.on())
    }
  }
}

कोई ऑटोमेशन चलाएं

execute() तरीके का इस्तेमाल करके, बनाए गए ऑटोमेशन को चलाएं:

createdAutomation.execute()

अगर ऑटोमेशन में मैन्युअल स्टार्टर है, तो execute() ऑटोमेशन को उस पॉइंट से शुरू करता है. साथ ही, मैन्युअल स्टार्टर से पहले के सभी नोड को अनदेखा कर देता है. अगर ऑटोमेशन में मैन्युअल तरीके से शुरू करने की सुविधा नहीं है, तो पहले स्टार्टर नोड के बाद वाले नोड से ऑटोमेशन शुरू होता है.

अगर execute() कार्रवाई पूरी नहीं हो पाती है, तो HomeException दिख सकता है. गड़बड़ी को ठीक करने का तरीका देखें.

किसी ऑटोमेशन को रोकना

stop() तरीके का इस्तेमाल करके, चल रहे ऑटोमेशन को बंद करना:


createdAutomation.stop()

अगर stop() कार्रवाई पूरी नहीं हो पाती है, तो HomeException दिख सकता है. गड़बड़ी को ठीक करने का तरीका देखें.

किसी स्ट्रक्चर के लिए ऑटोमेशन की सूची पाना

ऑटोमेशन, स्ट्रक्चर लेवल पर तय किए जाते हैं. ऑटोमेशन की Flow को ऐक्सेस करने के लिए, स्ट्रक्चर के automations() पर क्लिक करें:


import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
structure.automations().collect {
  println("Available automations:")
  for (automation in it) {
    println(String.format("%S %S", "$automation.id", "$automation.name"))
  }
}

इसके अलावा, इसे किसी स्थानीय Collection को असाइन करें:

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

var myAutomations: Collection<Automation> = emptyList()
myAutomations = structure.automations()

आईडी के हिसाब से कोई ऑटोमेशन पाना

ऑटोमेशन आईडी की मदद से ऑटोमेशन पाने के लिए, स्ट्रक्चर पर automations() तरीके को कॉल करें और आईडी से मैच करें:

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
  it.firstOrNull
    { automation -> automation.id == Id("automation-id") }
  }.firstOrNull()

जवाब:

// Here's how the automation looks like in the get response.
// Here, it's represented as if calling a println(automation.toString())

Automation(
  name = "automation-name",
  description = "automation-description",
  isActive = true,
  id = Id("automation@automation-id"),
  automationGraph = SequentialFlow(
    nodes = [
      Starter(
        entity="device@test-device",
        type="home.matter.0000.types.0101",
        trait="OnOff@6789..."),
      Action(
        entity="device@test-device",
        type="home.matter.0000.types.0101",
        trait="OnOff@8765...",
        command="on")
    ]))

नाम से कोई ऑटोमेशन ढूंढना

Kotlin में filter() तरीके का इस्तेमाल करके, एपीआई कॉल को और बेहतर बनाया जा सकता है. किसी ऑटोमेशन को उसके नाम से ढूंढने के लिए, स्ट्रक्चर के ऑटोमेशन की सूची पाएं और ऑटोमेशन के नाम के हिसाब से फ़िल्टर करें:

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().filter {
  it.name.equals("Sunset Blinds") }

किसी डिवाइस के लिए सभी ऑटोमेशन पाना

किसी डिवाइस से जुड़े सभी ऑटोमेशन की जानकारी पाने के लिए, नेस्टेड फ़िल्टर का इस्तेमाल करें. इससे हर ऑटोमेशन के automationGraph को स्कैन किया जा सकेगा:

import android.util.Log
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Automation.automationGraph
import com.google.home.automation.Node
import com.google.home.automation.ParallelFlow
import com.google.home.automation.SelectFlow
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.automation.StateReader

...

fun collectDescendants(node: Node): List<Node> {
  val d: MutableList<Node> = mutableListOf(node)

  val children: List<Node> =
    when (node) {
      is SequentialFlow -> node.nodes
      is ParallelFlow -> node.nodes
      is SelectFlow -> node.nodes
      else -> emptyList()
    }
  for (c in children) {
    d += collectDescendants(c)
  }
  return d
}

val myDeviceId = "device@452f78ce8-0143-84a-7e32-1d99ab54c83a"
val structure = homeManager.structures().list().single()
val automations =
  structure.automations().first().filter {
    automation: Automation ->
    collectDescendants(automation.automationGraph!!).any { node: Node ->
      when (node) {
        is Starter -> node.entity.id.id == myDeviceId
        is StateReader -> node.entity.id.id == myDeviceId
        is Action -> node.entity.id.id == myDeviceId
        else -> false
      }
    }
  }

किसी ऑटोमेशन को अपडेट करना

किसी ऑटोमेशन के मेटाडेटा को अपडेट करने के लिए, उसके update() तरीके को कॉल करें. साथ ही, उसे एक लैम्ब्डा एक्सप्रेशन पास करें, जो मेटाडेटा सेट करता है:

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
  it.firstOrNull
    { automation -> automation.id == Id("automation-id") }
  }.firstOrNull()
automation.update { this.name = "Flashing lights 2" }

update() तरीके से, ऑटोमेशन ग्राफ़ को पूरी तरह से बदला जा सकता है. हालांकि, ग्राफ़ के हर नोड में बदलाव नहीं किया जा सकता. हर नोड में बदलाव करने से गड़बड़ियां हो सकती हैं, क्योंकि नोड एक-दूसरे पर निर्भर होते हैं. अगर आपको ऑटोमेशन के लॉजिक में बदलाव करना है, तो नया ग्राफ़ जनरेट करें और मौजूदा ग्राफ़ को पूरी तरह से बदलें.

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
val automation: Automation = structure.automations().mapNotNull {
  it.firstOrNull
    { automation -> automation.id == Id("automation-id") }
  }.firstOrNull()
automation.update {
  this.automationGraph = sequential {
    val laundryWasherCompletionEvent =
      starter<_>(laundryWasher, LaundryWasherDevice, OperationCompletionEvent)
    condition {
      expression =
        laundryWasherCompletionEvent.completionErrorCode equals
          // UByte 0x00u means NoError
          0x00u
    }
    action(speaker, SpeakerDevice) { command(AssistantBroadcast.broadcast("laundry is done")) }
    }
  }
}

कोई ऑटोमेशन मिटाना

किसी ऑटोमेशन को मिटाने के लिए, स्ट्रक्चर के deleteAutomation() तरीके का इस्तेमाल करें. किसी ऑटोमेशन को उसके आईडी का इस्तेमाल करके मिटाना होगा.

import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure

...

val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().first()
structure.deleteAutomation(automation.id)

अगर मिटाने की प्रोसेस पूरी नहीं हो पाती है, तो HomeException दिख सकता है. गड़बड़ी को ठीक करना देखें.

डिवाइस को मिटाने से ऑटोमेशन पर पड़ने वाला असर

अगर कोई उपयोगकर्ता किसी ऐसे डिवाइस को मिटा देता है जिसका इस्तेमाल ऑटोमेशन में किया जाता है, तो मिटाया गया डिवाइस किसी भी स्टार्टर को ट्रिगर नहीं कर सकता. साथ ही, ऑटोमेशन उस डिवाइस से एट्रिब्यूट नहीं पढ़ पाएगा या उसे निर्देश नहीं दे पाएगा. उदाहरण के लिए, अगर कोई उपयोगकर्ता अपने होम से किसी OccupancySensorDevice को मिटा देता है और किसी ऑटोमेशन में ऐसा स्टार्टर है जो OccupancySensorDevice पर निर्भर है, तो वह स्टार्टर अब ऑटोमेशन को चालू नहीं कर पाएगा.