Android-এ অটোমেশন তৈরি করা

Android-এর জন্য Home API-এর মাধ্যমে অটোমেশন API অ্যাক্সেস করা যেতে পারে, তবে যেহেতু তাদের এন্ট্রি পয়েন্ট একটি স্ট্রাকচারের মাধ্যমে হয়, তাই সেগুলি ব্যবহার করার আগে স্ট্রাকচারে অনুমতি দিতে হবে।

কোনও স্ট্রাকচারের জন্য অনুমতি দেওয়া হয়ে গেলে, আপনার অ্যাপে এইসব প্যাকেজ ইমপোর্ট করুন:


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

একটি স্ট্রাকচারে নিম্নলিখিত অটোমেশন-নির্দিষ্ট পদ্ধতি সহ একটি HasAutomations ইন্টারফেস থাকে:

API বিবরণ
automations() হোমে দেখানো সব অটোমেশনের তালিকা। Home API-এর মাধ্যমে আপনার তৈরি করা অটোমেশনই শুধু ফিরিয়ে দেওয়া হয়।
createAutomation(automation) কোনও স্ট্রাকচারের জন্য অটোমেশন ইনস্ট্যান্স তৈরি করুন।
deleteAutomation(automationId) আইডি দিয়ে অটোমেশন ইনস্ট্যান্স মুছুন।

অটোমেশন তৈরি করুন

হোমের ইনস্ট্যান্স তৈরি করার পরে এবং ব্যবহারকারীর কাছ থেকে অনুমতি পাওয়ার পরে, স্ট্রাকচার এবং ডিভাইস(গুলি) পান:

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

তারপর অটোমেশন DSL ব্যবহার করে আপনার অটোমেশনের লজিক নির্ধারণ করুন। Home API-তে, Automation ইন্টারফেসের মাধ্যমে অটোমেশন দেখানো হয়। এই ইন্টারফেসে প্রপার্টির একটি সেট রয়েছে:

  • মেটাডেটা, যেমন নাম ও বিবরণ।
  • ফ্ল্যাগ যা ইঙ্গিত করে, যেমন, অটোমেশনটি কার্যকর করা যাবে কিনা।
  • অটোমেশনের লজিক আছে এমন নোডের তালিকা, যাকে অটোমেশন গ্রাফ বলা হয়, automationGraph প্রপার্টি দ্বারা দেখানো হয়।

automationGraph, ডিফল্ট হিসেবে, SequentialFlow ধরনের, যা এমন একটি ক্লাস যেটিতে ক্রমানুসারে এক্সিকিউট করা নোডের তালিকা থাকে। প্রতিটি নোড অটোমেশনের একটি এলিমেন্টকে দেখায়, যেমন স্টার্টার, শর্ত বা অ্যাকশন।

অটোমেশনে একটি name এবং description অ্যাসাইন করুন।

অটোমেশন তৈরি করার সময় isActive ফ্ল্যাগ ডিফল্ট হিসেবে true-এ সেট করা থাকে, তাই আপনি যদি অটোমেশন বন্ধ রাখতে না চান, তাহলে এই ফ্ল্যাগ আলাদা করে সেট করার প্রয়োজন নেই। সেই পরিস্থিতিতে, তৈরি করার সময় ফ্ল্যাগটি false-এ সেট করুন।

DraftAutomation ইন্টারফেস অটোমেশন তৈরি করার জন্য ব্যবহার করা হয়, এবং Automation ইন্টারফেস রিট্রিভ করার জন্য ব্যবহার করা হয়। যেমন, এখানে একটি অটোমেশনের জন্য অটোমেশন DSL দেওয়া হল যা অন্য ডিভাইস চালু হলে একটি ডিভাইস চালু করে:

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

অটোমেশন DSL সংজ্ঞায়িত করা হয়ে গেলে, DraftAutomation ইনস্ট্যান্স তৈরি করতে এটি createAutomation() মেথডে পাস করুন:

val createdAutomation = structure.createAutomation(automation)

এখান থেকে, আপনি অটোমেশনে অন্যান্য সব অটোমেশন পদ্ধতি ব্যবহার করতে পারবেন, যেমন execute(), stop() এবং update()।

যাচাইকরণ সংক্রান্ত সমস্যা

অটোমেশন তৈরি করার সময় যাচাইকরণে পাস না করলে, সমস্যা সম্পর্কে তথ্য প্রদান করার জন্য একটি সতর্কতা বা ত্রুটি মেসেজ দেখানো হয়। আরও তথ্যের জন্য, ValidationIssueType রেফারেন্স দেখুন।

এমনকি createAutomation() কোনও ব্যতিক্রম না দেখিয়ে সম্পূর্ণ হলেও, তৈরি করা অটোমেশনটি সঠিক বা কার্যকর নাও হতে পারে। ব্যাকএন্ড ভুল অটোমেশন ড্রাফ্ট সেভ করার অনুমতি দেয় (যেমন, কোনও ব্যবহারকারী যদি পরিচিত ফেস ডিটেকশনের মতো প্রয়োজনীয় সম্মতি না দিয়ে থাকেন বা ডিভাইসের ক্ষমতা না থাকে)।

আপনাকে সবসময় isValid যাচাই করতে হবে এবং validationIssues চেক করতে হবে Automation ইনস্ট্যান্স:

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-এ অটোমেশন ডিজাইন করুন পৃষ্ঠায় বর্ণিত কাল্পনিক অটোমেশনের অংশগুলি বাস্তবায়ন করতে ব্যবহার করা যেতে পারে।

সহজ অটোমেশন

সকাল ৮:০০টায় ব্লাইন্ড উপরে তোলার অটোমেশন এইভাবে প্রয়োগ করা যেতে পারে:

// 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() পদ্ধতি ব্যবহার করে API কল আরও রিফাইন করা যেতে পারে। নাম অনুযায়ী অটোমেশন পেতে, হোমে দেখানো অটোমেশন পান এবং অটোমেশনের নাম অনুযায়ী ফিল্টার করুন:

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-এর উপর নির্ভর করে এমন স্টার্টার থাকলে, সেই স্টার্টার আর অটোমেশন অ্যাক্টিভেট করতে পারবে না।