ควบคุมอุปกรณ์ภายนอก

ใน Android 11 ขึ้นไป ฟีเจอร์การควบคุมอุปกรณ์สำหรับการเข้าถึงด่วน ช่วยให้ผู้ใช้ดูและควบคุมอุปกรณ์ภายนอกได้อย่างรวดเร็ว เช่น ไฟ ตัวควบคุมอุณหภูมิ และกล้อง จากความสามารถของผู้ใช้ภายใน 3 การโต้ตอบจาก Launcher เริ่มต้น OEM ของอุปกรณ์จะเป็นผู้เลือก Launcher ที่จะใช้ ตัวรวบรวมอุปกรณ์ เช่น Google Home และแอปของผู้ให้บริการบุคคลที่สามสามารถ แสดงอุปกรณ์ในพื้นที่นี้ได้ หน้านี้จะแสดงวิธีแสดง การควบคุมอุปกรณ์ในพื้นที่นี้และลิงก์กับการควบคุมแอป

รูปที่ 1 พื้นที่ควบคุมอุปกรณ์ใน UI ของ Android

หากต้องการเพิ่มการรองรับนี้ ให้สร้างและประกาศ ControlsProviderService สร้าง การควบคุมที่แอปของคุณรองรับตามประเภทการควบคุมที่กำหนดไว้ล่วงหน้า จากนั้นสร้าง ผู้เผยแพร่สำหรับการควบคุมเหล่านี้

ส่วนติดต่อผู้ใช้

อุปกรณ์จะแสดงในส่วนระบบควบคุมอุปกรณ์เป็นวิดเจ็ตที่สร้างจากเทมเพลต วิดเจ็ตควบคุมอุปกรณ์ 5 รายการพร้อมให้ใช้งานดังที่แสดงในรูปต่อไปนี้

วิดเจ็ตสวิตช์
สลับ
เปิด/ปิดด้วยวิดเจ็ตแถบเลื่อน
สลับด้วยแถบเลื่อน
วิดเจ็ตช่วง
ช่วง (เปิดหรือปิดไม่ได้)
วิดเจ็ตปุ่มเปิด/ปิดแบบไม่มีสถานะ
สลับแบบไม่เก็บสถานะ
วิดเจ็ตแผงอุณหภูมิ (ปิด)
แผงอุณหภูมิ (ปิด)
รูปที่ 2 คอลเล็กชันวิดเจ็ตที่ใช้เทมเพลต

การแตะวิดเจ็ตค้างไว้จะนำคุณไปยังแอปเพื่อให้ควบคุมได้ละเอียดยิ่งขึ้น คุณปรับแต่งไอคอนและสีในวิดเจ็ตแต่ละรายการได้ แต่เพื่อประสบการณ์ของผู้ใช้ที่ดีที่สุด ให้ใช้ไอคอนและสีเริ่มต้นหากชุดเริ่มต้นตรงกับอุปกรณ์

รูปภาพแสดงวิดเจ็ตแผงอุณหภูมิ (เปิด)
รูปที่ 3 เปิดวิดเจ็ตแผงอุณหภูมิ

สร้างบริการ

ส่วนนี้แสดงวิธีสร้าง ControlsProviderService บริการนี้จะบอก UI ของระบบ Android ว่าแอปของคุณมีตัวควบคุมอุปกรณ์ ที่ต้องแสดงในส่วนตัวควบคุมอุปกรณ์ของ UI ของ Android

ControlsProviderService API นี้ถือว่าผู้ใช้มีความคุ้นเคยกับสตรีมแบบรีแอกทีฟตามที่กำหนดไว้ในโปรเจ็กต์ Reactive Streams GitHub และนำไปใช้ในอินเทอร์เฟซ Java 9 Flow API สร้างขึ้นโดยอิงตามแนวคิดต่อไปนี้

  • ผู้เผยแพร่: แอปพลิเคชันของคุณคือผู้เผยแพร่
  • ผู้ติดตาม: UI ของระบบคือผู้ติดตามและสามารถขอตัวควบคุมจำนวนหนึ่งจากผู้เผยแพร่โฆษณาได้
  • การสมัครใช้บริการ: กรอบเวลาที่ผู้เผยแพร่เนื้อหาสามารถส่งข้อมูลอัปเดตไปยัง UI ของระบบ ทั้งผู้เผยแพร่เนื้อหาและผู้ติดตามสามารถปิดหน้าต่างนี้ได้

ประกาศบริการ

แอปของคุณต้องประกาศบริการ เช่น MyCustomControlService ในไฟล์ Manifest ของแอป

บริการต้องมีตัวกรอง Intent สำหรับ ControlsProviderService ฟิลเตอร์นี้ช่วยให้แอปพลิเคชันมีส่วนร่วมในการควบคุม UI ของระบบ

นอกจากนี้ คุณยังต้องมี label ที่แสดงในการควบคุมใน UI ของระบบด้วย

ตัวอย่างต่อไปนี้แสดงวิธีกำหนดบริการ

<service
    android:name="MyCustomControlService"
    android:label="My Custom Controls"
    android:permission="android.permission.BIND_CONTROLS"
    android:exported="true"
    >
    <intent-filter>
      <action android:name="android.service.controls.ControlsProviderService" />
    </intent-filter>
</service>

จากนั้นสร้างไฟล์ Kotlin ใหม่ชื่อ MyCustomControlService.kt และทำให้ไฟล์ ขยาย ControlsProviderService() ดังนี้

Kotlin

    class MyCustomControlService : ControlsProviderService() {
        ...
    }
    

Java

    public class MyCustomJavaControlService extends ControlsProviderService {
        ...
    }
    

เลือกประเภทการควบคุมที่ถูกต้อง

API มีเมธอด Builder เพื่อสร้างตัวควบคุม หากต้องการป้อนข้อมูลใน เครื่องมือสร้าง ให้ระบุอุปกรณ์ที่ต้องการควบคุมและวิธีที่ผู้ใช้โต้ตอบ กับอุปกรณ์ ทำตามขั้นตอนต่อไปนี้

  1. เลือกประเภทอุปกรณ์ที่ตัวควบคุมแสดง คลาส DeviceTypes คือ การแจงนับอุปกรณ์ที่รองรับทั้งหมด ระบบจะใช้ประเภทเพื่อกำหนดไอคอนและสีสำหรับอุปกรณ์ใน UI
  2. กำหนดชื่อที่แสดงต่อผู้ใช้ ตำแหน่งอุปกรณ์ เช่น ห้องครัว และองค์ประกอบข้อความอื่นๆ ของ UI ที่เชื่อมโยงกับการควบคุม
  3. เลือกเทมเพลตที่ดีที่สุดเพื่อรองรับการโต้ตอบของผู้ใช้ ระบบจะกำหนด ControlTemplate ให้กับตัวควบคุมจากแอปพลิเคชัน เทมเพลตนี้จะแสดงสถานะการควบคุมต่อผู้ใช้โดยตรง รวมถึงวิธีการป้อนข้อมูลที่มีอยู่ ซึ่งก็คือ ControlAction ตารางต่อไปนี้แสดงเทมเพลตบางส่วนที่มีอยู่และการดำเนินการที่รองรับ
เทมเพลต การดำเนินการ คำอธิบาย
ControlTemplate.getNoTemplateObject() None แอปพลิเคชันอาจใช้ข้อความนี้เพื่อสื่อสารข้อมูลเกี่ยวกับการควบคุม แต่ผู้ใช้จะโต้ตอบกับข้อความนี้ไม่ได้
ToggleTemplate BooleanAction แสดงตัวควบคุมที่สลับระหว่างสถานะเปิดใช้และปิดใช้ได้ ออบเจ็กต์ BooleanAction มีฟิลด์ที่เปลี่ยน เพื่อแสดงสถานะใหม่ที่ขอเมื่อผู้ใช้แตะตัวควบคุม
RangeTemplate FloatAction แสดงวิดเจ็ตแถบเลื่อนที่มีค่าต่ำสุด สูงสุด และขั้นที่ระบุ เมื่อ ผู้ใช้โต้ตอบกับแถบเลื่อน ให้ส่งออบเจ็กต์ FloatAction ใหม่กลับไปยังแอปพลิเคชันพร้อมค่าที่อัปเดต
ToggleRangeTemplate BooleanAction, FloatAction เทมเพลตนี้เป็นการผสมผสานระหว่าง ToggleTemplate และ RangeTemplate โดยรองรับทั้งเหตุการณ์การแตะและแถบเลื่อน เช่น เพื่อควบคุมหลอดไฟที่หรี่แสงได้
TemperatureControlTemplate ModeAction, BooleanAction, FloatAction นอกเหนือจากการห่อหุ้มการดำเนินการก่อนหน้าแล้ว เทมเพลตนี้ยังช่วยให้ ผู้ใช้ตั้งค่าโหมดต่างๆ ได้ เช่น ทำความร้อน ทำความเย็น ทำความร้อน/ทำความเย็น อีโค หรือปิด
StatelessTemplate CommandAction ใช้เพื่อระบุตัวควบคุมที่มีความสามารถในการสัมผัส แต่ไม่สามารถระบุสถานะได้ เช่น รีโมตทีวี IR คุณสามารถใช้ เทมเพลตนี้เพื่อกำหนดกิจวัตรหรือมาโคร ซึ่งเป็นการรวบรวมการควบคุม และการเปลี่ยนแปลงสถานะ

คุณสามารถใช้ข้อมูลนี้เพื่อสร้างการควบคุมได้ดังนี้

  • ใช้คลาสบิลเดอร์ Control.StatelessBuilder เมื่อไม่ทราบสถานะของตัวควบคุม
  • ใช้คลาสเครื่องมือสร้าง Control.StatefulBuilder เมื่อทราบสถานะของตัวควบคุม

ตัวอย่างเช่น หากต้องการควบคุมหลอดไฟอัจฉริยะและตัวควบคุมอุณหภูมิ ให้เพิ่มค่าคงที่ต่อไปนี้ ลงใน MyCustomControlService

Kotlin

    private const val LIGHT_ID = 1234
    private const val LIGHT_TITLE = "My fancy light"
    private const val LIGHT_TYPE = DeviceTypes.TYPE_LIGHT
    private const val THERMOSTAT_ID = 5678
    private const val THERMOSTAT_TITLE = "My fancy thermostat"
    private const val THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT
 
    class MyCustomControlService : ControlsProviderService() {
      ...
    }
    

Java

    public class MyCustomJavaControlService extends ControlsProviderService {
 
    private final int LIGHT_ID = 1337;
    private final String LIGHT_TITLE = "My fancy light";
    private final int LIGHT_TYPE = DeviceTypes.TYPE_LIGHT;
    private final int THERMOSTAT_ID = 1338;
    private final String THERMOSTAT_TITLE = "My fancy thermostat";
    private final int THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT;
 
    ...
    }
    

สร้างผู้เผยแพร่โฆษณาสำหรับการควบคุม

หลังจากสร้างตัวควบคุมแล้ว ตัวควบคุมจะต้องมีผู้เผยแพร่ ผู้เผยแพร่เนื้อหาจะแจ้งให้ UI ของระบบทราบว่ามีตัวควบคุมอยู่ คลาส ControlsProviderService มีเมธอดของผู้เผยแพร่โฆษณา 2 รายการที่คุณต้องลบล้างในโค้ดแอปพลิเคชัน

  • createPublisherForAllAvailable(): สร้าง Publisher สำหรับการควบคุมทั้งหมดที่มีในแอป ใช้ Control.StatelessBuilder() เพื่อสร้างออบเจ็กต์ Control สำหรับผู้เผยแพร่โฆษณารายนี้
  • createPublisherFor(): สร้าง Publisher สำหรับรายการการควบคุมที่ระบุ ตามตัวระบุสตริง ใช้ Control.StatefulBuilder เพื่อสร้างออบเจ็กต์ Control เหล่านี้ เนื่องจากผู้เผยแพร่โฆษณาต้องกำหนดสถานะให้กับแต่ละตัวควบคุม

สร้างผู้เผยแพร่

เมื่อแอปเผยแพร่ตัวควบคุมไปยัง UI ของระบบเป็นครั้งแรก แอปจะไม่ทราบ สถานะของตัวควบคุมแต่ละรายการ การรับสถานะอาจเป็นการดำเนินการที่ใช้เวลานาน ซึ่งเกี่ยวข้องกับการข้ามหลายครั้งในเครือข่ายของผู้ให้บริการอุปกรณ์ ใช้วิธี createPublisherForAllAvailable() เพื่อโฆษณาการควบคุมที่ใช้ได้ในระบบ วิธีนี้ใช้คลาสบิลเดอร์ Control.StatelessBuilder เนื่องจากสถานะของตัวควบคุมแต่ละรายการไม่ทราบ

เมื่อการควบคุมปรากฏใน UI ของ Android แล้ว ผู้ใช้จะเลือกการควบคุมที่ชื่นชอบได้

หากต้องการใช้ Kotlin Coroutines เพื่อสร้าง ControlsProviderService ให้เพิ่มการอ้างอิงใหม่ ลงใน build.gradle ดังนี้

Groovy

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4"
}

Kotlin

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4")
}

เมื่อซิงค์ไฟล์ Gradle แล้ว ให้เพิ่มข้อมูลโค้ดต่อไปนี้ลงใน Service เพื่อ ใช้ createPublisherForAllAvailable()

Kotlin

    class MyCustomControlService : ControlsProviderService() {
 
      override fun createPublisherForAllAvailable(): Flow.Publisher =
          flowPublish {
              send(createStatelessControl(LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE))
              send(createStatelessControl(THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE))
          }
 
      private fun createStatelessControl(id: Int, title: String, type: Int): Control {
          val intent = Intent(this, MainActivity::class.java)
              .putExtra(EXTRA_MESSAGE, title)
              .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
          val action = PendingIntent.getActivity(
              this,
              id,
              intent,
              PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
          )
 
          return Control.StatelessBuilder(id.toString(), action)
              .setTitle(title)
              .setDeviceType(type)
              .build()
      }
 
          override fun createPublisherFor(controlIds: List): Flow.Publisher {
           TODO()
        }
 
        override fun performControlAction(
            controlId: String,
            action: ControlAction,
            consumer: Consumer
        ) {
            TODO()
        }
    }
    

Java

    public class MyCustomJavaControlService extends ControlsProviderService {
 
        private final int LIGHT_ID = 1337;
        private final String LIGHT_TITLE = "My fancy light";
        private final int LIGHT_TYPE = DeviceTypes.TYPE_LIGHT;
        private final int THERMOSTAT_ID = 1338;
        private final String THERMOSTAT_TITLE = "My fancy thermostat";
        private final int THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT;
 
        private boolean toggleState = false;
        private float rangeState = 18f;
        private final Map<String, ReplayProcessor> controlFlows = new HashMap<>();
 
        @NonNull
        @Override
        public Flow.Publisher createPublisherForAllAvailable() {
            List controls = new ArrayList<>();
            controls.add(createStatelessControl(LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE));
            controls.add(createStatelessControl(THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE));
            return FlowAdapters.toFlowPublisher(Flowable.fromIterable(controls));
        }
 
        @NonNull
        @Override
        public Flow.Publisher createPublisherFor(@NonNull List controlIds) {
            ReplayProcessor updatePublisher = ReplayProcessor.create();
 
            controlIds.forEach(control -> {
                controlFlows.put(control, updatePublisher);
                updatePublisher.onNext(createLight());
                updatePublisher.onNext(createThermostat());
            });
 
            return FlowAdapters.toFlowPublisher(updatePublisher);
        }
    }
    

ปัดเมนูระบบลง แล้วหาปุ่มการควบคุมอุปกรณ์ตามที่แสดงใน รูปที่ 4

รูปภาพแสดง UI ของระบบสำหรับการควบคุมอุปกรณ์
รูปที่ 4 การควบคุมอุปกรณ์ในเมนูระบบ

การแตะการควบคุมอุปกรณ์จะนำคุณไปยังหน้าจอที่ 2 ซึ่งคุณสามารถเลือก แอปได้ เมื่อเลือกแอปแล้ว คุณจะเห็นว่าข้อมูลโค้ดก่อนหน้าสร้างเมนูระบบที่กำหนดเองซึ่งแสดงการควบคุมใหม่ของคุณได้อย่างไร ดังที่แสดงในรูปที่ 5

รูปภาพแสดงเมนูระบบที่มีการควบคุมไฟและตัวควบคุมอุณหภูมิ
รูปที่ 5 การควบคุมหลอดไฟและตัวควบคุมอุณหภูมิที่จะเพิ่ม

ตอนนี้ ให้ใช้เมธอด createPublisherFor() โดยเพิ่มข้อมูลต่อไปนี้ลงใน Service

Kotlin

    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.IO + job)
    private val controlFlows = mutableMapOf<String, MutableSharedFlow>()
 
    private var toggleState = false
    private var rangeState = 18f
 
    override fun createPublisherFor(controlIds: List): Flow.Publisher {
        val flow = MutableSharedFlow(replay = 2, extraBufferCapacity = 2)
 
        controlIds.forEach { controlFlows[it] = flow }
 
        scope.launch {
            delay(1000) // Retrieving the toggle state.
            flow.tryEmit(createLight())
 
            delay(1000) // Retrieving the range state.
            flow.tryEmit(createThermostat())
 
        }
        return flow.asPublisher()
    }
 
    private fun createLight() = createStatefulControl(
        LIGHT_ID,
        LIGHT_TITLE,
        LIGHT_TYPE,
        toggleState,
        ToggleTemplate(
            LIGHT_ID.toString(),
            ControlButton(
                toggleState,
                toggleState.toString().uppercase(Locale.getDefault())
            )
        )
    )
 
    private fun createThermostat() = createStatefulControl(
        THERMOSTAT_ID,
        THERMOSTAT_TITLE,
        THERMOSTAT_TYPE,
        rangeState,
        RangeTemplate(
            THERMOSTAT_ID.toString(),
            15f,
            25f,
            rangeState,
            0.1f,
            "%1.1f"
        )
    )
 
    private fun  createStatefulControl(id: Int, title: String, type: Int, state: T, template: ControlTemplate): Control {
        val intent = Intent(this, MainActivity::class.java)
            .putExtra(EXTRA_MESSAGE, "$title $state")
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        val action = PendingIntent.getActivity(
            this,
            id,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
 
        return Control.StatefulBuilder(id.toString(), action)
            .setTitle(title)
            .setDeviceType(type)
            .setStatus(Control.STATUS_OK)
            .setControlTemplate(template)
            .build()
    }
 
    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
 
    

Java

    @NonNull
    @Override
    public Flow.Publisher createPublisherFor(@NonNull List controlIds) {
        ReplayProcessor updatePublisher = ReplayProcessor.create();
 
        controlIds.forEach(control -> {
            controlFlows.put(control, updatePublisher);
            updatePublisher.onNext(createLight());
            updatePublisher.onNext(createThermostat());
        });
 
        return FlowAdapters.toFlowPublisher(updatePublisher);
    }
 
    private Control createStatelessControl(int id, String title, int type) {
        Intent intent = new Intent(this, MainActivity.class)
                .putExtra(EXTRA_MESSAGE, title)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent action = PendingIntent.getActivity(
                this,
                id,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );
 
        return new Control.StatelessBuilder(id + "", action)
                .setTitle(title)
                .setDeviceType(type)
                .build();
    }
 
    private Control createLight() {
        return createStatefulControl(
                LIGHT_ID,
                LIGHT_TITLE,
                LIGHT_TYPE,
                toggleState,
                new ToggleTemplate(
                        LIGHT_ID + "",
                        new ControlButton(
                                toggleState,
                                String.valueOf(toggleState).toUpperCase(Locale.getDefault())
                        )
                )
        );
    }
 
    private Control createThermostat() {
        return createStatefulControl(
                THERMOSTAT_ID,
                THERMOSTAT_TITLE,
                THERMOSTAT_TYPE,
                rangeState,
                new RangeTemplate(
                        THERMOSTAT_ID + "",
                        15f,
                        25f,
                        rangeState,
                        0.1f,
                        "%1.1f"
                )
        );
    }
 
    private  Control createStatefulControl(int id, String title, int type, T state, ControlTemplate template) {
        Intent intent = new Intent(this, MainActivity.class)
                .putExtra(EXTRA_MESSAGE, "$title $state")
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent action = PendingIntent.getActivity(
                this,
                id,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );
 
        return new Control.StatefulBuilder(id + "", action)
                .setTitle(title)
                .setDeviceType(type)
                .setStatus(Control.STATUS_OK)
                .setControlTemplate(template)
                .build();
    }
    

ในตัวอย่างนี้ เมธอด createPublisherFor() มีการใช้งานแบบจำลองของสิ่งที่แอปของคุณต้องทำ ได้แก่ สื่อสารกับอุปกรณ์เพื่อดึงข้อมูลสถานะ และส่งสถานะดังกล่าวไปยังระบบ

เมธอด createPublisherFor() ใช้โครูทีนและโฟลว์ของ Kotlin เพื่อให้เป็นไปตาม API ของ Reactive Streams ที่จำเป็นโดยทำดังนี้

  1. สร้าง Flow
  2. รอ 1 วินาที
  3. สร้างและปล่อยสถานะของหลอดไฟอัจฉริยะ
  4. รออีก 1 วินาที
  5. สร้างและส่งสถานะของตัวควบคุมอุณหภูมิ

จัดการการดำเนินการ

performControlAction() วิธีนี้จะส่งสัญญาณเมื่อผู้ใช้โต้ตอบกับตัวควบคุมที่เผยแพร่ ประเภทของ ControlAction ที่ส่งจะเป็นตัวกำหนดการดำเนินการ ดำเนินการที่เหมาะสมกับการควบคุมที่ระบุ จากนั้นอัปเดตสถานะ ของอุปกรณ์ใน UI ของ Android

หากต้องการทำให้ตัวอย่างเสร็จสมบูรณ์ ให้เพิ่มข้อมูลต่อไปนี้ลงใน Service

Kotlin

    override fun performControlAction(
        controlId: String,
        action: ControlAction,
        consumer: Consumer
    ) {
        controlFlows[controlId]?.let { flow ->
            when (controlId) {
                LIGHT_ID.toString() -> {
                    consumer.accept(ControlAction.RESPONSE_OK)
                    if (action is BooleanAction) toggleState = action.newState
                    flow.tryEmit(createLight())
                }
                THERMOSTAT_ID.toString() -> {
                    consumer.accept(ControlAction.RESPONSE_OK)
                    if (action is FloatAction) rangeState = action.newValue
                    flow.tryEmit(createThermostat())
                }
                else -> consumer.accept(ControlAction.RESPONSE_FAIL)
            }
        } ?: consumer.accept(ControlAction.RESPONSE_FAIL)
    }
    

Java

    @Override
    public void performControlAction(@NonNull String controlId, @NonNull ControlAction action, @NonNull Consumer consumer) {
        ReplayProcessor processor = controlFlows.get(controlId);
        if (processor == null) return;
 
        if (controlId.equals(LIGHT_ID + "")) {
            consumer.accept(ControlAction.RESPONSE_OK);
            if (action instanceof BooleanAction) toggleState = ((BooleanAction) action).getNewState();
            processor.onNext(createLight());
        }
        if (controlId.equals(THERMOSTAT_ID + "")) {
            consumer.accept(ControlAction.RESPONSE_OK);
            if (action instanceof FloatAction) rangeState = ((FloatAction) action).getNewValue()
            processor.onNext(createThermostat());
        }
    }
    

เรียกใช้แอป เข้าถึงเมนูการควบคุมอุปกรณ์ และดูการควบคุมหลอดไฟและ เทอร์โมสตัท

รูปภาพแสดงการควบคุมไฟและตัวควบคุมอุณหภูมิ
รูปที่ 6 การควบคุมไฟและตัวควบคุมอุณหภูมิ