1616
1717from .api import Api
1818from .capability import (
19+ ATTRIBUTE_OFF_VALUES ,
1920 ATTRIBUTE_ON_VALUES ,
2021 Attribute ,
2122 Capability ,
@@ -52,6 +53,11 @@ def hex_to_hs(color_hex: str) -> (int, int):
5253 return round (hsv [0 ] * 100 , 3 ), round (hsv [1 ] * 100 , 3 )
5354
5455
56+ def bool_to_value (attribute : str , value : bool ) -> str :
57+ """Convert bool value to ON/OFF value of given attribute."""
58+ return ATTRIBUTE_ON_VALUES [attribute ] if value else ATTRIBUTE_OFF_VALUES [attribute ]
59+
60+
5561class Command :
5662 """Define common commands."""
5763
@@ -78,6 +84,22 @@ class Command:
7884 set_thermostat_fan_mode = "setThermostatFanMode"
7985 set_thermostat_mode = "setThermostatMode"
8086 unlock = "unlock"
87+ mute = "mute"
88+ unmute = "unmute"
89+ set_volume = "setVolume"
90+ volume_up = "volumeUp"
91+ volume_down = "volumeDown"
92+ play = "play"
93+ pause = "pause"
94+ stop = "stop"
95+ fast_forward = "fastForward"
96+ rewind = "rewind"
97+ set_input_source = "setInputSource"
98+ set_playback_shuffle = "setPlaybackShuffle"
99+ set_playback_repeat_mode = "setPlaybackRepeatMode"
100+ set_tv_channel = "setTvChannel"
101+ channel_up = "channelUp"
102+ channel_down = "channelDown"
81103
82104
83105class Device :
@@ -308,7 +330,7 @@ def switch(self) -> bool:
308330 @switch .setter
309331 def switch (self , value : bool ):
310332 """Set the value of the switch attribute."""
311- status_value = ATTRIBUTE_ON_VALUES [ Attribute .switch ] if value else "off"
333+ status_value = bool_to_value ( Attribute .switch , value )
312334 self .update_attribute_value (Attribute .switch , status_value )
313335
314336 @property
@@ -588,6 +610,97 @@ def three_axis(self) -> Optional[Tuple[int, int, int]]:
588610 """Get the three axis attribute."""
589611 return self ._attributes [Attribute .three_axis ].value
590612
613+ @property
614+ def mute (self ) -> bool :
615+ """Get the mute attribute."""
616+ return self .is_on (Attribute .mute )
617+
618+ @mute .setter
619+ def mute (self , value : bool ):
620+ """Set the mute attribute."""
621+ status_value = bool_to_value (Attribute .mute , value )
622+ self .update_attribute_value (Attribute .mute , status_value )
623+
624+ @property
625+ def volume (self ) -> int :
626+ """Get the volume attribute."""
627+ return self ._attributes [Attribute .volume ].value
628+
629+ @volume .setter
630+ def volume (self , value : float ):
631+ """Set the volume attribute, scaled 0-100."""
632+ if not 0 <= value <= 100 :
633+ raise ValueError ("value must be scaled between 0-100." )
634+ self .update_attribute_value (Attribute .volume , value )
635+
636+ @property
637+ def playback_status (self ) -> str :
638+ """Get the playbackStatus attribute."""
639+ return self ._attributes [Attribute .playback_status ].value
640+
641+ @playback_status .setter
642+ def playback_status (self , value : str ):
643+ """Set the playbackStatus attribute."""
644+ self .update_attribute_value (Attribute .playback_status , value )
645+
646+ @property
647+ def input_source (self ) -> str :
648+ """Get the inputSource attribute."""
649+ return self ._attributes [Attribute .input_source ].value
650+
651+ @input_source .setter
652+ def input_source (self , value : str ):
653+ """Set the volume attribute."""
654+ if value not in self .supported_input_sources :
655+ raise ValueError ("value must be supported." )
656+ self .update_attribute_value (Attribute .input_source , value )
657+
658+ @property
659+ def supported_input_sources (self ) -> Sequence [str ]:
660+ """Get the supportedInputSources attribute."""
661+ value = self ._attributes [Attribute .supported_input_sources ].value
662+ if "value" in value :
663+ return value ["value" ]
664+ return value
665+
666+ @property
667+ def playback_shuffle (self ) -> bool :
668+ """Get the playbackShuffle attribute."""
669+ return self .is_on (Attribute .playback_shuffle )
670+
671+ @playback_shuffle .setter
672+ def playback_shuffle (self , value : bool ):
673+ """Set the playbackShuffle attribute."""
674+ status_value = bool_to_value (Attribute .playback_shuffle , value )
675+ self .update_attribute_value (Attribute .playback_shuffle , status_value )
676+
677+ @property
678+ def playback_repeat_mode (self ) -> str :
679+ """Get the playbackRepeatMode attribute."""
680+ return self ._attributes [Attribute .playback_repeat_mode ].value
681+
682+ @playback_repeat_mode .setter
683+ def playback_repeat_mode (self , value : str ):
684+ """Set the playbackRepeatMode attribute."""
685+ if value not in ["all" , "off" , "one" ]:
686+ raise ValueError ("value must be one of: all, off, one" )
687+ self .update_attribute_value (Attribute .playback_repeat_mode , value )
688+
689+ @property
690+ def tv_channel (self ) -> str :
691+ """Get the tvChannel attribute."""
692+ return self ._attributes [Attribute .tv_channel ].value
693+
694+ @tv_channel .setter
695+ def tv_channel (self , value : str ):
696+ """Set the tvChannel attribute."""
697+ self .update_attribute_value (Attribute .tv_channel , value )
698+
699+ @property
700+ def media_title (self ) -> bool :
701+ """Get the trackDescription attribute."""
702+ return self ._attributes ["trackDescription" ].value
703+
591704
592705class DeviceStatus (DeviceStatusBase ):
593706 """Define the device status."""
@@ -1061,6 +1174,182 @@ async def set_air_flow_direction(
10611174 self .status .update_attribute_value (Attribute .air_flow_direction , direction )
10621175 return result
10631176
1177+ async def mute (
1178+ self , set_status : bool = False , * , component_id : str = "main"
1179+ ) -> bool :
1180+ """Call the mute command."""
1181+ result = await self .command (component_id , Capability .audio_mute , Command .mute )
1182+ if result and set_status :
1183+ self .status .mute = True
1184+ return result
1185+
1186+ async def unmute (
1187+ self , set_status : bool = False , * , component_id : str = "main"
1188+ ) -> bool :
1189+ """Call the unmute command."""
1190+ result = await self .command (component_id , Capability .audio_mute , Command .unmute )
1191+ if result and set_status :
1192+ self .status .mute = False
1193+ return result
1194+
1195+ async def set_volume (
1196+ self , volume : int , set_status : bool = False , * , component_id : str = "main"
1197+ ) -> bool :
1198+ """Call the setVolume command."""
1199+ result = await self .command (
1200+ component_id , Capability .audio_volume , Command .set_volume , [volume ]
1201+ )
1202+ if result and set_status :
1203+ self .status .volume = volume
1204+ return result
1205+
1206+ async def volume_up (
1207+ self , set_status : bool = False , * , component_id : str = "main"
1208+ ) -> bool :
1209+ """Call the volumeUp command."""
1210+ result = await self .command (
1211+ component_id , Capability .audio_volume , Command .volume_up
1212+ )
1213+ if result and set_status :
1214+ self .status .volume = min (self .status .volume + 1 , 100 )
1215+ return result
1216+
1217+ async def volume_down (
1218+ self , set_status : bool = False , * , component_id : str = "main"
1219+ ) -> bool :
1220+ """Call the volumeDown command."""
1221+ result = await self .command (
1222+ component_id , Capability .audio_volume , Command .volume_down
1223+ )
1224+ if result and set_status :
1225+ self .status .volume = max (self .status .volume - 1 , 0 )
1226+ return result
1227+
1228+ async def play (
1229+ self , set_status : bool = False , * , component_id : str = "main"
1230+ ) -> bool :
1231+ """Call the play command."""
1232+ result = await self .command (
1233+ component_id , Capability .media_playback , Command .play
1234+ )
1235+ if result and set_status :
1236+ self .status .playback_status = "play"
1237+ return result
1238+
1239+ async def pause (
1240+ self , set_status : bool = False , * , component_id : str = "main"
1241+ ) -> bool :
1242+ """Call the pause command."""
1243+ result = await self .command (
1244+ component_id , Capability .media_playback , Command .pause
1245+ )
1246+ if result and set_status :
1247+ self .status .playback_status = "pause"
1248+ return result
1249+
1250+ async def stop (
1251+ self , set_status : bool = False , * , component_id : str = "main"
1252+ ) -> bool :
1253+ """Call the stop command."""
1254+ result = await self .command (
1255+ component_id , Capability .media_playback , Command .stop
1256+ )
1257+ if result and set_status :
1258+ self .status .playback_status = "stop"
1259+ return result
1260+
1261+ async def fast_forward (
1262+ self , set_status : bool = False , * , component_id : str = "main"
1263+ ) -> bool :
1264+ """Call the fastForward command."""
1265+ result = await self .command (
1266+ component_id , Capability .media_playback , Command .fast_forward
1267+ )
1268+ if result and set_status :
1269+ self .status .playback_status = "fast forward"
1270+ return result
1271+
1272+ async def rewind (
1273+ self , set_status : bool = False , * , component_id : str = "main"
1274+ ) -> bool :
1275+ """Call the rewind command."""
1276+ result = await self .command (
1277+ component_id , Capability .media_playback , Command .rewind
1278+ )
1279+ if result and set_status :
1280+ self .status .playback_status = "rewind"
1281+ return result
1282+
1283+ async def set_input_source (
1284+ self , source : str , set_status : bool = False , * , component_id : str = "main"
1285+ ) -> bool :
1286+ """Call the setInputSource command."""
1287+ result = await self .command (
1288+ component_id ,
1289+ Capability .media_input_source ,
1290+ Command .set_input_source ,
1291+ [source ],
1292+ )
1293+ if result and set_status :
1294+ self .status .input_source = source
1295+ return result
1296+
1297+ async def set_playback_shuffle (
1298+ self , shuffle : bool , set_status : bool = False , * , component_id : str = "main"
1299+ ) -> bool :
1300+ """Call the setPlaybackShuffle command."""
1301+ shuffle_value = bool_to_value (Attribute .playback_shuffle , shuffle )
1302+ result = await self .command (
1303+ component_id ,
1304+ Capability .media_playback_shuffle ,
1305+ Command .set_playback_shuffle ,
1306+ [shuffle_value ],
1307+ )
1308+ if result and set_status :
1309+ self .status .playback_shuffle = shuffle
1310+ return result
1311+
1312+ async def set_repeat (
1313+ self , repeat : str , set_status : bool = False , * , component_id : str = "main"
1314+ ) -> bool :
1315+ """Call the setPlaybackRepeatMode command."""
1316+ result = await self .command (
1317+ component_id ,
1318+ Capability .media_playback_repeat ,
1319+ Command .set_playback_repeat_mode ,
1320+ [repeat ],
1321+ )
1322+ if result and set_status :
1323+ self .status .playback_repeat_mode = repeat
1324+ return result
1325+
1326+ async def set_tv_channel (
1327+ self , channel : str , set_status : bool = False , * , component_id : str = "main"
1328+ ) -> bool :
1329+ """Call the setTvChannel command."""
1330+ result = await self .command (
1331+ component_id , Capability .tv_channel , Command .set_tv_channel , [channel ]
1332+ )
1333+ if result and set_status :
1334+ self .status .tv_channel = channel
1335+ return result
1336+
1337+ async def channel_up (
1338+ self , set_status : bool = False , * , component_id : str = "main"
1339+ ) -> bool :
1340+ """Call the channelUp command."""
1341+ return await self .command (
1342+ component_id , Capability .tv_channel , Command .channel_up
1343+ )
1344+
1345+ async def channel_down (
1346+ self , set_status : bool = False , * , component_id : str = "main"
1347+ ) -> bool :
1348+ """Call the channelDown command."""
1349+ return await self .command (
1350+ component_id , Capability .tv_channel , Command .channel_down
1351+ )
1352+
10641353 @property
10651354 def status (self ):
10661355 """Get the status entity of the device."""
0 commit comments