Skip to content

Commit 67f0c43

Browse files
authored
Merge pull request #49 from kzosabe/add-pseudo-status-infrared-remotes
Add pseudo status for remote devices
2 parents 302a103 + cbe6c7a commit 67f0c43

7 files changed

Lines changed: 411 additions & 134 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ DeviceStatus(device_id='ABCDE', device_type='Meter', device_name='Meter 0', hub_
107107
```
108108

109109
This function allows you to get the status of a device.
110-
Note that only physical devices can be targeted, not virtual infrared remote devices.
110+
Note that only the physical device returns accurate results, while the virtual infrared remote device is inaccurate,
111+
storing the results of the most recent operation.
111112

112113
Please refer to the following official document to know what kind of information can be obtained from each device.
113114

switchbot_client/devices/base.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
from __future__ import annotations
22

3-
from abc import ABC
3+
from abc import ABC, abstractmethod
44
from dataclasses import dataclass
55
from typing import TYPE_CHECKING, Optional
66

77
if TYPE_CHECKING:
88
from switchbot_client import SwitchBotClient
9+
from switchbot_client.devices import DeviceStatus
910

1011

1112
@dataclass()
12-
class SwitchBotDevice(ABC):
13+
class SwitchBotDeviceBase:
1314
client: SwitchBotClient
1415
device_id: str
1516
device_type: str
@@ -27,6 +28,8 @@ def __post_init__(self):
2728
if self.hub_device_id in ["FFFFFFFFFFFF", "000000000000"]:
2829
self.hub_device_id = None
2930

31+
32+
class SwitchBotDevice(ABC, SwitchBotDeviceBase):
3033
def command(
3134
self, command: str, parameter: str = None, command_type: str = None
3235
) -> SwitchBotCommandResult:
@@ -35,6 +38,10 @@ def command(
3538
)
3639
return SwitchBotCommandResult(response.status_code, response.message, response.body)
3740

41+
@abstractmethod
42+
def status(self) -> DeviceStatus:
43+
pass
44+
3845
def __repr__(self):
3946
data = {
4047
"device_id": self.device_id,

switchbot_client/devices/remote.py

Lines changed: 166 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,43 @@
66
from switchbot_client.types import APIRemoteDeviceObject
77

88
from .base import SwitchBotCommandResult, SwitchBotDevice
9+
from .status import PseudoRemoteDeviceStatus
910

1011
if TYPE_CHECKING:
1112
from switchbot_client import SwitchBotClient
1213

1314

1415
class SwitchBotRemoteDevice(SwitchBotDevice):
15-
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
16-
device_id = device["deviceId"]
17-
remote_type = device["remoteType"]
18-
device_name = device["deviceName"]
19-
hub_device_id = device["hubDeviceId"]
20-
super().__init__(client, device_id, remote_type, device_name, hub_device_id, True)
16+
def __init__(
17+
self,
18+
client: SwitchBotClient,
19+
device: APIRemoteDeviceObject,
20+
pseudo_status: PseudoRemoteDeviceStatus,
21+
):
22+
super().__init__(
23+
client,
24+
device["deviceId"],
25+
device["remoteType"],
26+
device["deviceName"],
27+
device["hubDeviceId"],
28+
True,
29+
)
2130
self.device = device
31+
self.pseudo_status = pseudo_status
32+
self._validate_pseudo_status()
2233

2334
def turn_on(self) -> SwitchBotCommandResult:
24-
return self.command(ControlCommand.VirtualInfrared.TURN_ON)
35+
response = self.command(ControlCommand.VirtualInfrared.TURN_ON)
36+
self.pseudo_status.set_power("on")
37+
return response
2538

2639
def turn_off(self) -> SwitchBotCommandResult:
27-
return self.command(ControlCommand.VirtualInfrared.TURN_OFF)
40+
response = self.command(ControlCommand.VirtualInfrared.TURN_OFF)
41+
self.pseudo_status.set_power("off")
42+
return response
43+
44+
def status(self) -> PseudoRemoteDeviceStatus:
45+
return self.pseudo_status
2846

2947
@staticmethod
3048
def create_by_api_object( # noqa
@@ -83,6 +101,20 @@ def _check_remote_type(self, expected_device_type: str):
83101
f"expected: {expected_device_type}, actual: {self.device_type}"
84102
)
85103

104+
def _validate_pseudo_status(self):
105+
if (
106+
self.pseudo_status.device_id != self.device_id
107+
or self.pseudo_status.device_type != self.device_type
108+
or self.pseudo_status.device_name != self.device_name
109+
or self.pseudo_status.hub_device_id != self.hub_device_id
110+
):
111+
raise RuntimeError(
112+
f"Illegal pseudo status. "
113+
f"expected: {self.device_id}, {self.device_type}, "
114+
f"{self.device_name}, {self.hub_device_id}, "
115+
f"actual: {self.pseudo_status}"
116+
)
117+
86118

87119
class AirConditioner(SwitchBotRemoteDevice):
88120
class Parameters:
@@ -99,7 +131,15 @@ class Parameters:
99131
POWER_OFF = "off"
100132

101133
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
102-
super().__init__(client, device)
134+
pseudo_status = PseudoRemoteDeviceStatus(
135+
device_id=device["deviceId"],
136+
device_type=device["remoteType"],
137+
device_name=device["deviceName"],
138+
hub_device_id=device["hubDeviceId"],
139+
power=None,
140+
raw_data={},
141+
)
142+
super().__init__(client, device, pseudo_status)
103143
self._check_remote_type(RemoteType.AIR_CONDITIONER)
104144

105145
# remote devices don't have status fetch commands
@@ -175,7 +215,15 @@ def set_fan_speed(self, fan_speed: int) -> SwitchBotCommandResult:
175215

176216
class TV(SwitchBotRemoteDevice):
177217
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
178-
super().__init__(client, device)
218+
pseudo_status = PseudoRemoteDeviceStatus(
219+
device_id=device["deviceId"],
220+
device_type=device["remoteType"],
221+
device_name=device["deviceName"],
222+
hub_device_id=device["hubDeviceId"],
223+
power=None,
224+
raw_data={},
225+
)
226+
super().__init__(client, device, pseudo_status)
179227
self._check_remote_type(RemoteType.TV)
180228

181229
@staticmethod
@@ -204,7 +252,15 @@ def channel_sub(self) -> SwitchBotCommandResult:
204252

205253
class Light(SwitchBotRemoteDevice):
206254
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
207-
super().__init__(client, device)
255+
pseudo_status = PseudoRemoteDeviceStatus(
256+
device_id=device["deviceId"],
257+
device_type=device["remoteType"],
258+
device_name=device["deviceName"],
259+
hub_device_id=device["hubDeviceId"],
260+
power=None,
261+
raw_data={},
262+
)
263+
super().__init__(client, device, pseudo_status)
208264
self._check_remote_type(RemoteType.LIGHT)
209265

210266
def brightness_up(self) -> SwitchBotCommandResult:
@@ -221,7 +277,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> Light:
221277

222278
class IPTVStreamer(SwitchBotRemoteDevice):
223279
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
224-
super().__init__(client, device)
280+
pseudo_status = PseudoRemoteDeviceStatus(
281+
device_id=device["deviceId"],
282+
device_type=device["remoteType"],
283+
device_name=device["deviceName"],
284+
hub_device_id=device["hubDeviceId"],
285+
power=None,
286+
raw_data={},
287+
)
288+
super().__init__(client, device, pseudo_status)
225289
self._check_remote_type(RemoteType.IPTV_STREAMER)
226290

227291
@staticmethod
@@ -244,7 +308,15 @@ def channel_sub(self) -> SwitchBotCommandResult:
244308

245309
class SetTopBox(SwitchBotRemoteDevice):
246310
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
247-
super().__init__(client, device)
311+
pseudo_status = PseudoRemoteDeviceStatus(
312+
device_id=device["deviceId"],
313+
device_type=device["remoteType"],
314+
device_name=device["deviceName"],
315+
hub_device_id=device["hubDeviceId"],
316+
power=None,
317+
raw_data={},
318+
)
319+
super().__init__(client, device, pseudo_status)
248320
self._check_remote_type(RemoteType.SET_TOP_BOX)
249321

250322
@staticmethod
@@ -267,7 +339,15 @@ def channel_sub(self) -> SwitchBotCommandResult:
267339

268340
class DVD(SwitchBotRemoteDevice):
269341
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
270-
super().__init__(client, device)
342+
pseudo_status = PseudoRemoteDeviceStatus(
343+
device_id=device["deviceId"],
344+
device_type=device["remoteType"],
345+
device_name=device["deviceName"],
346+
hub_device_id=device["hubDeviceId"],
347+
power=None,
348+
raw_data={},
349+
)
350+
super().__init__(client, device, pseudo_status)
271351
self._check_remote_type(RemoteType.DVD)
272352

273353
@staticmethod
@@ -302,7 +382,15 @@ def stop(self) -> SwitchBotCommandResult:
302382

303383
class Fan(SwitchBotRemoteDevice):
304384
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
305-
super().__init__(client, device)
385+
pseudo_status = PseudoRemoteDeviceStatus(
386+
device_id=device["deviceId"],
387+
device_type=device["remoteType"],
388+
device_name=device["deviceName"],
389+
hub_device_id=device["hubDeviceId"],
390+
power=None,
391+
raw_data={},
392+
)
393+
super().__init__(client, device, pseudo_status)
306394
self._check_remote_type(RemoteType.FAN)
307395

308396
@staticmethod
@@ -328,7 +416,15 @@ def high_speed(self) -> SwitchBotCommandResult:
328416

329417
class Projector(SwitchBotRemoteDevice):
330418
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
331-
super().__init__(client, device)
419+
pseudo_status = PseudoRemoteDeviceStatus(
420+
device_id=device["deviceId"],
421+
device_type=device["remoteType"],
422+
device_name=device["deviceName"],
423+
hub_device_id=device["hubDeviceId"],
424+
power=None,
425+
raw_data={},
426+
)
427+
super().__init__(client, device, pseudo_status)
332428
self._check_remote_type(RemoteType.PROJECTOR)
333429

334430
@staticmethod
@@ -339,7 +435,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> Projector:
339435

340436
class Camera(SwitchBotRemoteDevice):
341437
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
342-
super().__init__(client, device)
438+
pseudo_status = PseudoRemoteDeviceStatus(
439+
device_id=device["deviceId"],
440+
device_type=device["remoteType"],
441+
device_name=device["deviceName"],
442+
hub_device_id=device["hubDeviceId"],
443+
power=None,
444+
raw_data={},
445+
)
446+
super().__init__(client, device, pseudo_status)
343447
self._check_remote_type(RemoteType.CAMERA)
344448

345449
@staticmethod
@@ -350,7 +454,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> Camera:
350454

351455
class AirPurifier(SwitchBotRemoteDevice):
352456
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
353-
super().__init__(client, device)
457+
pseudo_status = PseudoRemoteDeviceStatus(
458+
device_id=device["deviceId"],
459+
device_type=device["remoteType"],
460+
device_name=device["deviceName"],
461+
hub_device_id=device["hubDeviceId"],
462+
power=None,
463+
raw_data={},
464+
)
465+
super().__init__(client, device, pseudo_status)
354466
self._check_remote_type(RemoteType.AIR_PURIFIER)
355467

356468
@staticmethod
@@ -361,7 +473,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> AirPurifier:
361473

362474
class Speaker(SwitchBotRemoteDevice):
363475
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
364-
super().__init__(client, device)
476+
pseudo_status = PseudoRemoteDeviceStatus(
477+
device_id=device["deviceId"],
478+
device_type=device["remoteType"],
479+
device_name=device["deviceName"],
480+
hub_device_id=device["hubDeviceId"],
481+
power=None,
482+
raw_data={},
483+
)
484+
super().__init__(client, device, pseudo_status)
365485
self._check_remote_type(RemoteType.SPEAKER)
366486

367487
@staticmethod
@@ -396,7 +516,15 @@ def stop(self) -> SwitchBotCommandResult:
396516

397517
class WaterHeater(SwitchBotRemoteDevice):
398518
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
399-
super().__init__(client, device)
519+
pseudo_status = PseudoRemoteDeviceStatus(
520+
device_id=device["deviceId"],
521+
device_type=device["remoteType"],
522+
device_name=device["deviceName"],
523+
hub_device_id=device["hubDeviceId"],
524+
power=None,
525+
raw_data={},
526+
)
527+
super().__init__(client, device, pseudo_status)
400528
self._check_remote_type(RemoteType.WATER_HEATER)
401529

402530
@staticmethod
@@ -407,7 +535,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> WaterHeater:
407535

408536
class VacuumCleaner(SwitchBotRemoteDevice):
409537
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
410-
super().__init__(client, device)
538+
pseudo_status = PseudoRemoteDeviceStatus(
539+
device_id=device["deviceId"],
540+
device_type=device["remoteType"],
541+
device_name=device["deviceName"],
542+
hub_device_id=device["hubDeviceId"],
543+
power=None,
544+
raw_data={},
545+
)
546+
super().__init__(client, device, pseudo_status)
411547
self._check_remote_type(RemoteType.VACUUM_CLEANER)
412548

413549
@staticmethod
@@ -418,7 +554,15 @@ def create_by_id(client: SwitchBotClient, device_id: str) -> VacuumCleaner:
418554

419555
class Others(SwitchBotRemoteDevice):
420556
def __init__(self, client: SwitchBotClient, device: APIRemoteDeviceObject):
421-
super().__init__(client, device)
557+
pseudo_status = PseudoRemoteDeviceStatus(
558+
device_id=device["deviceId"],
559+
device_type=device["remoteType"],
560+
device_name=device["deviceName"],
561+
hub_device_id=device["hubDeviceId"],
562+
power=None,
563+
raw_data={},
564+
)
565+
super().__init__(client, device, pseudo_status)
422566
self._check_remote_type(RemoteType.OTHERS)
423567

424568
@staticmethod

0 commit comments

Comments
 (0)