andrewsayre: Make sure you're formatting the code with `black` :)
PiotrMachowski: That's much easier 😃 
PiotrMachowski: Is there any specific reason to keep so many classes (`Command`, `Device`, `DeviceStatusBase`, `DeviceStatus`, `DeviceEntity`) in one big file (`device.py`?
PiotrMachowski: @andrewsayre any progress here?
PiotrMachowski: @andrewsayre 
andrewsayre: Sorry for the delay, I will review this week and move this forward.
PiotrMachowski: No pressure :)
diff --git a/pysmartthings/capability.py b/pysmartthings/capability.py
index 579e06c..155cc89 100644
--- a/pysmartthings/capability.py
+++ b/pysmartthings/capability.py
@@ -358,6 +358,7 @@ ATTRIBUTE_ON_VALUES = {
     Attribute.filter_status: "replace",
     Attribute.motion: "active",
     Attribute.mute: "muted",
+    Attribute.playback_shuffle: "enabled",
     Attribute.presence: "present",
     Attribute.sound: "detected",
     Attribute.switch: "on",
@@ -365,3 +366,18 @@ ATTRIBUTE_ON_VALUES = {
     Attribute.valve: "open",
     Attribute.water: "wet",
 }
+
+ATTRIBUTE_OFF_VALUES = {
+    Attribute.acceleration: "inactive",
+    Attribute.contact: "closed",
+    Attribute.filter_status: "normal",
+    Attribute.motion: "inactive",
+    Attribute.mute: "unmuted",
+    Attribute.playback_shuffle: "disabled",
+    Attribute.presence: "not present",
+    Attribute.sound: "not detected",
+    Attribute.switch: "off",
+    Attribute.tamper: "clear",
+    Attribute.valve: "closed",
+    Attribute.water: "dry",
+}
diff --git a/pysmartthings/device.py b/pysmartthings/device.py
index 878ff59..a15af6b 100644
--- a/pysmartthings/device.py
+++ b/pysmartthings/device.py
@@ -16,6 +16,7 @@ from typing import (
 
 from .api import Api
 from .capability import (
+    ATTRIBUTE_OFF_VALUES,
     ATTRIBUTE_ON_VALUES,
     Attribute,
     Capability,
@@ -52,6 +53,11 @@ def hex_to_hs(color_hex: str) -> (int, int):
     return round(hsv[0] * 100, 3), round(hsv[1] * 100, 3)
 
 
+def bool_to_value(attribute: str, value: bool) -> str:
+    """Convert bool value to ON/OFF value of given attribute."""
+    return ATTRIBUTE_ON_VALUES[attribute] if value else ATTRIBUTE_OFF_VALUES[attribute]
+
+
 class Command:
     """Define common commands."""
 
@@ -78,6 +84,22 @@ class Command:
     set_thermostat_fan_mode = "setThermostatFanMode"
     set_thermostat_mode = "setThermostatMode"
     unlock = "unlock"
+    mute = "mute"
+    unmute = "unmute"
+    set_volume = "setVolume"
+    volume_up = "volumeUp"
+    volume_down = "volumeDown"
+    play = "play"
+    pause = "pause"
+    stop = "stop"
+    fast_forward = "fastForward"
+    rewind = "rewind"
+    set_input_source = "setInputSource"
+    set_playback_shuffle = "setPlaybackShuffle"
+    set_playback_repeat_mode = "setPlaybackRepeatMode"
+    set_tv_channel = "setTvChannel"
+    channel_up = "channelUp"
+    channel_down = "channelDown"
 
 
 class Device:
@@ -308,7 +330,7 @@ class DeviceStatusBase:
     @switch.setter
     def switch(self, value: bool):
         """Set the value of the switch attribute."""
-        status_value = ATTRIBUTE_ON_VALUES[Attribute.switch] if value else "off"
+        status_value = bool_to_value(Attribute.switch, value)
         self.update_attribute_value(Attribute.switch, status_value)
 
     @property
@@ -588,6 +610,97 @@ class DeviceStatusBase:
         """Get the three axis attribute."""
         return self._attributes[Attribute.three_axis].value
 
+    @property
+    def mute(self) -> bool:
+        """Get the mute attribute."""
+        return self.is_on(Attribute.mute)
+
+    @mute.setter
+    def mute(self, value: bool):
+        """Set the mute attribute."""
+        status_value = bool_to_value(Attribute.mute, value)
+        self.update_attribute_value(Attribute.mute, status_value)
+
+    @property
+    def volume(self) -> int:
+        """Get the volume attribute."""
+        return self._attributes[Attribute.volume].value
+
+    @volume.setter
+    def volume(self, value: float):
+        """Set the volume attribute, scaled 0-100."""
+        if not 0 <= value <= 100:
+            raise ValueError("value must be scaled between 0-100.")
+        self.update_attribute_value(Attribute.volume, value)
+
+    @property
+    def playback_status(self) -> str:
+        """Get the playbackStatus attribute."""
+        return self._attributes[Attribute.playback_status].value
+
+    @playback_status.setter
+    def playback_status(self, value: str):
+        """Set the playbackStatus attribute."""
+        self.update_attribute_value(Attribute.playback_status, value)
+
+    @property
+    def input_source(self) -> str:
+        """Get the inputSource attribute."""
+        return self._attributes[Attribute.input_source].value
+
+    @input_source.setter
+    def input_source(self, value: str):
+        """Set the volume attribute."""
+        if value not in self.supported_input_sources:
+            raise ValueError("value must be supported.")
+        self.update_attribute_value(Attribute.input_source, value)
+
+    @property
+    def supported_input_sources(self) -> Sequence[str]:
+        """Get the supportedInputSources attribute."""
+        value = self._attributes[Attribute.supported_input_sources].value
+        if "value" in value:
+            return value["value"]
+        return value
+
+    @property
+    def playback_shuffle(self) -> bool:
+        """Get the playbackShuffle attribute."""
+        return self.is_on(Attribute.playback_shuffle)
+
+    @playback_shuffle.setter
+    def playback_shuffle(self, value: bool):
+        """Set the playbackShuffle attribute."""
+        status_value = bool_to_value(Attribute.playback_shuffle, value)
+        self.update_attribute_value(Attribute.playback_shuffle, status_value)
+
+    @property
+    def playback_repeat_mode(self) -> str:
+        """Get the playbackRepeatMode attribute."""
+        return self._attributes[Attribute.playback_repeat_mode].value
+
+    @playback_repeat_mode.setter
+    def playback_repeat_mode(self, value: str):
+        """Set the playbackRepeatMode attribute."""
+        if value not in ["all", "off", "one"]:
+            raise ValueError("value must be one of: all, off, one")
+        self.update_attribute_value(Attribute.playback_repeat_mode, value)
+
+    @property
+    def tv_channel(self) -> str:
+        """Get the tvChannel attribute."""
+        return self._attributes[Attribute.tv_channel].value
+
+    @tv_channel.setter
+    def tv_channel(self, value: str):
+        """Set the tvChannel attribute."""
+        self.update_attribute_value(Attribute.tv_channel, value)
+
+    @property
+    def media_title(self) -> bool:
+        """Get the trackDescription attribute."""
+        return self._attributes["trackDescription"].value
+
 
 class DeviceStatus(DeviceStatusBase):
     """Define the device status."""
@@ -1061,6 +1174,182 @@ class DeviceEntity(Entity, Device):
             self.status.update_attribute_value(Attribute.air_flow_direction, direction)
         return result
 
+    async def mute(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the mute command."""
+        result = await self.command(component_id, Capability.audio_mute, Command.mute)
+        if result and set_status:
+            self.status.mute = True
+        return result
+
+    async def unmute(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the unmute command."""
+        result = await self.command(component_id, Capability.audio_mute, Command.unmute)
+        if result and set_status:
+            self.status.mute = False
+        return result
+
+    async def set_volume(
+        self, volume: int, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the setVolume command."""
+        result = await self.command(
+            component_id, Capability.audio_volume, Command.set_volume, [volume]
+        )
+        if result and set_status:
+            self.status.volume = volume
+        return result
+
+    async def volume_up(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the volumeUp command."""
+        result = await self.command(
+            component_id, Capability.audio_volume, Command.volume_up
+        )
+        if result and set_status:
+            self.status.volume = min(self.status.volume + 1, 100)
+        return result
+
+    async def volume_down(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the volumeDown command."""
+        result = await self.command(
+            component_id, Capability.audio_volume, Command.volume_down
+        )
+        if result and set_status:
+            self.status.volume = max(self.status.volume - 1, 0)
+        return result
+
+    async def play(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the play command."""
+        result = await self.command(
+            component_id, Capability.media_playback, Command.play
+        )
+        if result and set_status:
+            self.status.playback_status = "play"
+        return result
+
+    async def pause(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the pause command."""
+        result = await self.command(
+            component_id, Capability.media_playback, Command.pause
+        )
+        if result and set_status:
+            self.status.playback_status = "pause"
+        return result
+
+    async def stop(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the stop command."""
+        result = await self.command(
+            component_id, Capability.media_playback, Command.stop
+        )
+        if result and set_status:
+            self.status.playback_status = "stop"
+        return result
+
+    async def fast_forward(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the fastForward command."""
+        result = await self.command(
+            component_id, Capability.media_playback, Command.fast_forward
+        )
+        if result and set_status:
+            self.status.playback_status = "fast forward"
+        return result
+
+    async def rewind(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the rewind command."""
+        result = await self.command(
+            component_id, Capability.media_playback, Command.rewind
+        )
+        if result and set_status:
+            self.status.playback_status = "rewind"
+        return result
+
+    async def set_input_source(
+        self, source: str, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the setInputSource command."""
+        result = await self.command(
+            component_id,
+            Capability.media_input_source,
+            Command.set_input_source,
+            [source],
+        )
+        if result and set_status:
+            self.status.input_source = source
+        return result
+
+    async def set_playback_shuffle(
+        self, shuffle: bool, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the setPlaybackShuffle command."""
+        shuffle_value = bool_to_value(Attribute.playback_shuffle, shuffle)
+        result = await self.command(
+            component_id,
+            Capability.media_playback_shuffle,
+            Command.set_playback_shuffle,
+            [shuffle_value],
+        )
+        if result and set_status:
+            self.status.playback_shuffle = shuffle
+        return result
+
+    async def set_repeat(
+        self, repeat: str, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the setPlaybackRepeatMode command."""
+        result = await self.command(
+            component_id,
+            Capability.media_playback_repeat,
+            Command.set_playback_repeat_mode,
+            [repeat],
+        )
+        if result and set_status:
+            self.status.playback_repeat_mode = repeat
+        return result
+
+    async def set_tv_channel(
+        self, channel: str, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the setTvChannel command."""
+        result = await self.command(
+            component_id, Capability.tv_channel, Command.set_tv_channel, [channel]
+        )
+        if result and set_status:
+            self.status.tv_channel = channel
+        return result
+
+    async def channel_up(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the channelUp command."""
+        return await self.command(
+            component_id, Capability.tv_channel, Command.channel_up
+        )
+
+    async def channel_down(
+        self, set_status: bool = False, *, component_id: str = "main"
+    ) -> bool:
+        """Call the channelDown command."""
+        return await self.command(
+            component_id, Capability.tv_channel, Command.channel_down
+        )
+
     @property
     def status(self):
         """Get the status entity of the device."""

Media devices support
Would it be possible to add support for media devices (TVs, soundbars, etc,)?
It seems that there already is some basic support for them (appropriate capabilities and attribute names), but there are no dedicated methods for executing commands nor attributes to retrieve current value.

https://smartthings.developer.samsung.com/docs/api-ref/capabilities.html#Media-Playback
diff --git a/tests/json/device_command_post_channel_down.json b/tests/json/device_command_post_channel_down.json
new file mode 100644
index 0000000..ace9c63
--- /dev/null
+++ b/tests/json/device_command_post_channel_down.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "tvChannel",
+      "command": "channelDown"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_channel_up.json b/tests/json/device_command_post_channel_up.json
new file mode 100644
index 0000000..6c6c83b
--- /dev/null
+++ b/tests/json/device_command_post_channel_up.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "tvChannel",
+      "command": "channelUp"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_fast_forward.json b/tests/json/device_command_post_fast_forward.json
new file mode 100644
index 0000000..8ab327f
--- /dev/null
+++ b/tests/json/device_command_post_fast_forward.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlayback",
+      "command": "fastForward"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_mute.json b/tests/json/device_command_post_mute.json
new file mode 100644
index 0000000..b216edc
--- /dev/null
+++ b/tests/json/device_command_post_mute.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "audioMute",
+      "command": "mute"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_pause.json b/tests/json/device_command_post_pause.json
new file mode 100644
index 0000000..dbce642
--- /dev/null
+++ b/tests/json/device_command_post_pause.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlayback",
+      "command": "pause"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_play.json b/tests/json/device_command_post_play.json
new file mode 100644
index 0000000..1136968
--- /dev/null
+++ b/tests/json/device_command_post_play.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlayback",
+      "command": "play"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_rewind.json b/tests/json/device_command_post_rewind.json
new file mode 100644
index 0000000..c0773da
--- /dev/null
+++ b/tests/json/device_command_post_rewind.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlayback",
+      "command": "rewind"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_set_input_source.json b/tests/json/device_command_post_set_input_source.json
new file mode 100644
index 0000000..16d921c
--- /dev/null
+++ b/tests/json/device_command_post_set_input_source.json
@@ -0,0 +1,10 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaInputSource",
+      "command": "setInputSource",
+      "arguments": ["Source"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_set_playback_shuffle.json b/tests/json/device_command_post_set_playback_shuffle.json
new file mode 100644
index 0000000..4864952
--- /dev/null
+++ b/tests/json/device_command_post_set_playback_shuffle.json
@@ -0,0 +1,10 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlaybackShuffle",
+      "command": "setPlaybackShuffle",
+      "arguments": ["enabled"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_set_repeat.json b/tests/json/device_command_post_set_repeat.json
new file mode 100644
index 0000000..5481a31
--- /dev/null
+++ b/tests/json/device_command_post_set_repeat.json
@@ -0,0 +1,10 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlaybackRepeat",
+      "command": "setPlaybackRepeatMode",
+      "arguments": ["all"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_set_tv_channel.json b/tests/json/device_command_post_set_tv_channel.json
new file mode 100644
index 0000000..9e13995
--- /dev/null
+++ b/tests/json/device_command_post_set_tv_channel.json
@@ -0,0 +1,10 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "tvChannel",
+      "command": "setTvChannel",
+      "arguments": ["Channel"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_set_volume.json b/tests/json/device_command_post_set_volume.json
new file mode 100644
index 0000000..3fd39cb
--- /dev/null
+++ b/tests/json/device_command_post_set_volume.json
@@ -0,0 +1,10 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "audioVolume",
+      "command": "setVolume",
+      "arguments": [10]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_stop.json b/tests/json/device_command_post_stop.json
new file mode 100644
index 0000000..87c328c
--- /dev/null
+++ b/tests/json/device_command_post_stop.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "mediaPlayback",
+      "command": "stop"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_unmute.json b/tests/json/device_command_post_unmute.json
new file mode 100644
index 0000000..a70ff66
--- /dev/null
+++ b/tests/json/device_command_post_unmute.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "audioMute",
+      "command": "unmute"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_volume_down.json b/tests/json/device_command_post_volume_down.json
new file mode 100644
index 0000000..ca77fb3
--- /dev/null
+++ b/tests/json/device_command_post_volume_down.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "audioVolume",
+      "command": "volumeDown"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/json/device_command_post_volume_up.json b/tests/json/device_command_post_volume_up.json
new file mode 100644
index 0000000..1fd4912
--- /dev/null
+++ b/tests/json/device_command_post_volume_up.json
@@ -0,0 +1,9 @@
+{
+  "commands": [
+    {
+      "component": "main",
+      "capability": "audioVolume",
+      "command": "volumeUp"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/test_device.py b/tests/test_device.py
index ae4a318..e008f7e 100644
--- a/tests/test_device.py
+++ b/tests/test_device.py
@@ -812,6 +812,398 @@ class TestDeviceEntity:
         assert await device.set_air_flow_direction("fixed", set_status=True)
         assert device.status.air_flow_direction == "fixed"
 
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_mute(api):
+        """Test the mute method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.mute = False
+        # Act
+        result = await device.mute()
+        # Assert
+        assert result
+        assert not device.status.mute
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_mute_update(api):
+        """Test the mute method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.mute = False
+        # Act
+        result = await device.mute(set_status=True)
+        # Assert
+        assert result
+        assert device.status.mute
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_unmute(api):
+        """Test the unmute method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.mute = True
+        # Act
+        result = await device.unmute()
+        # Assert
+        assert result
+        assert device.status.mute
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_unmute_update(api):
+        """Test the unmute method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.mute = True
+        # Act
+        result = await device.unmute(set_status=True)
+        # Assert
+        assert result
+        assert not device.status.mute
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_volume(api):
+        """Test the set_volume method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 9
+        # Act
+        result = await device.set_volume(volume=10)
+        # Assert
+        assert result
+        assert device.status.volume == 9
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_volume_update(api):
+        """Test the set_volume method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 9
+        # Act
+        result = await device.set_volume(volume=10, set_status=True)
+        # Assert
+        assert result
+        assert device.status.volume == 10
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_volume_up(api):
+        """Test the volume_up method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 9
+        # Act
+        result = await device.volume_up()
+        # Assert
+        assert result
+        assert device.status.volume == 9
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_volume_up_update(api):
+        """Test the volume_up method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 9
+        # Act
+        result = await device.volume_up(set_status=True)
+        # Assert
+        assert result
+        assert device.status.volume == 10
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_volume_down(api):
+        """Test the volume_down method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 11
+        # Act
+        result = await device.volume_down()
+        # Assert
+        assert result
+        assert device.status.volume == 11
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_volume_down_update(api):
+        """Test the volume_down method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.volume = 11
+        # Act
+        result = await device.volume_down(set_status=True)
+        # Assert
+        assert result
+        assert device.status.volume == 10
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_play(api):
+        """Test the play method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.play()
+        # Assert
+        assert result
+        assert device.status.playback_status is None
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_play_update(api):
+        """Test the play method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.play(set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_status == "play"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_pause(api):
+        """Test the pause method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.pause()
+        # Assert
+        assert result
+        assert device.status.playback_status is None
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_pause_update(api):
+        """Test the pause method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.pause(set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_status == "pause"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_stop(api):
+        """Test the stop method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.stop()
+        # Assert
+        assert result
+        assert device.status.playback_status is None
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_stop_update(api):
+        """Test the stop method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.stop(set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_status == "stop"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_fast_forward(api):
+        """Test the fast_forward method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.fast_forward()
+        # Assert
+        assert result
+        assert device.status.playback_status is None
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_fast_forward_update(api):
+        """Test the fast_forward method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.fast_forward(set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_status == "fast forward"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_rewind(api):
+        """Test the rewind method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.rewind()
+        # Assert
+        assert result
+        assert device.status.playback_status is None
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_rewind_update(api):
+        """Test the rewind method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_status = None
+        # Act
+        result = await device.rewind(set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_status == "rewind"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_input_source(api):
+        """Test the set_input_source method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.update_attribute_value(
+            Attribute.supported_input_sources, ["Source", "OtherSource"]
+        )
+        device.status.input_source = "OtherSource"
+        # Act
+        result = await device.set_input_source("Source")
+        # Assert
+        assert result
+        assert device.status.input_source == "OtherSource"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_input_source_update(api):
+        """Test the set_input_source method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.update_attribute_value(
+            Attribute.supported_input_sources, ["Source", "OtherSource"]
+        )
+        device.status.input_source = "OtherSource"
+        # Act
+        result = await device.set_input_source("Source", set_status=True)
+        # Assert
+        assert result
+        assert device.status.input_source == "Source"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_playback_shuffle(api):
+        """Test the set_playback_shuffle method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_shuffle = False
+        # Act
+        result = await device.set_playback_shuffle(shuffle=True)
+        # Assert
+        assert result
+        assert not device.status.playback_shuffle
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_playback_shuffle_update(api):
+        """Test the set_playback_shuffle method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_shuffle = False
+        # Act
+        result = await device.set_playback_shuffle(True, set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_shuffle
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_repeat(api):
+        """Test the set_repeat method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_repeat_mode = "off"
+        # Act
+        result = await device.set_repeat("all")
+        # Assert
+        assert result
+        assert device.status.playback_repeat_mode == "off"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_repeat_update(api):
+        """Test the set_repeat method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.playback_repeat_mode = "off"
+        # Act
+        result = await device.set_repeat("all", set_status=True)
+        # Assert
+        assert result
+        assert device.status.playback_repeat_mode == "all"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_tv_channel(api):
+        """Test the tv_channel method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.tv_channel = "OtherChannel"
+        # Act
+        result = await device.set_tv_channel("Channel")
+        # Assert
+        assert result
+        assert device.status.tv_channel == "OtherChannel"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_set_tv_channel_update(api):
+        """Test the tv_channel method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        device.status.tv_channel = "OtherChannel"
+        # Act
+        result = await device.set_tv_channel("Channel", set_status=True)
+        # Assert
+        assert result
+        assert device.status.tv_channel == "Channel"
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_channel_up(api):
+        """Test the channel_up method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        # Act
+        result = await device.channel_up()
+        # Assert
+        assert result
+
+    @staticmethod
+    @pytest.mark.asyncio
+    async def test_channel_down(api):
+        """Test the channel_down method."""
+        # Arrange
+        device = DeviceEntity(api, device_id=DEVICE_ID)
+        # Act
+        result = await device.channel_down()
+        # Assert
+        assert result
+
 
 class TestDeviceStatus:
     """Tests for the DeviceStatus class."""
@@ -1042,6 +1434,36 @@ class TestDeviceStatus:
             with pytest.raises(ValueError):
                 status.color = value
 
+    @staticmethod
+    def test_volume_range(api):
+        """Test the volume property's range."""
+        # Arrange
+        status = DeviceStatus(api, device_id=DEVICE_ID)
+        # Act/Assert
+        values = [-1, 101]
+        for value in values:
+            with pytest.raises(ValueError):
+                status.volume = value
+
+    @staticmethod
+    def test_input_source(api):
+        """Test the volume property's range."""
+        # Arrange
+        status = DeviceStatus(api, device_id=DEVICE_ID)
+        status.update_attribute_value(Attribute.supported_input_sources, "Source")
+        # Act/Assert
+        with pytest.raises(ValueError):
+            status.input_source = "INVALID"
+
+    @staticmethod
+    def test_playback_repeat_mode(api):
+        """Test the volume property's range."""
+        # Arrange
+        status = DeviceStatus(api, device_id=DEVICE_ID)
+        # Act/Assert
+        with pytest.raises(ValueError):
+            status.playback_repeat_mode = "INVALID"
+
     @staticmethod
     def test_is_on():
         """Tests the is_on method."""

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-asyncio pytest-cov pytest-timeout
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==3.6.2
async-timeout==3.0.1
attrs==24.2.0
certifi @ file:///croot/certifi_1671487769961/work/certifi
chardet==3.0.4
coverage==7.2.7
exceptiongroup==1.2.2
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
multidict==4.7.6
packaging==24.0
pluggy==1.2.0
-e git+https://github.com/andrewsayre/pysmartthings.git@a247ac8a776a4df1d4fda38fdf0e1c7c701e1054#egg=pysmartthings
pytest==7.4.4
pytest-asyncio==0.21.2
pytest-cov==4.1.0
pytest-timeout==2.3.1
tomli==2.0.1
typing_extensions==4.7.1
yarl==1.9.4
zipp==3.15.0

name: pysmartthings
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==3.6.2
      - async-timeout==3.0.1
      - attrs==24.2.0
      - chardet==3.0.4
      - coverage==7.2.7
      - exceptiongroup==1.2.2
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - multidict==4.7.6
      - packaging==24.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - pytest-asyncio==0.21.2
      - pytest-cov==4.1.0
      - pytest-timeout==2.3.1
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - yarl==1.9.4
      - zipp==3.15.0
prefix: /opt/conda/envs/pysmartthings

tests/test_device.py::TestDeviceEntity::test_mute tests/test_device.py::TestDeviceEntity::test_mute_update tests/test_device.py::TestDeviceEntity::test_unmute tests/test_device.py::TestDeviceEntity::test_unmute_update tests/test_device.py::TestDeviceEntity::test_set_volume tests/test_device.py::TestDeviceEntity::test_set_volume_update tests/test_device.py::TestDeviceEntity::test_volume_up tests/test_device.py::TestDeviceEntity::test_volume_up_update tests/test_device.py::TestDeviceEntity::test_volume_down tests/test_device.py::TestDeviceEntity::test_volume_down_update tests/test_device.py::TestDeviceEntity::test_play tests/test_device.py::TestDeviceEntity::test_play_update tests/test_device.py::TestDeviceEntity::test_pause tests/test_device.py::TestDeviceEntity::test_pause_update tests/test_device.py::TestDeviceEntity::test_stop tests/test_device.py::TestDeviceEntity::test_stop_update tests/test_device.py::TestDeviceEntity::test_fast_forward tests/test_device.py::TestDeviceEntity::test_fast_forward_update tests/test_device.py::TestDeviceEntity::test_rewind tests/test_device.py::TestDeviceEntity::test_rewind_update tests/test_device.py::TestDeviceEntity::test_set_input_source tests/test_device.py::TestDeviceEntity::test_set_input_source_update tests/test_device.py::TestDeviceEntity::test_set_playback_shuffle tests/test_device.py::TestDeviceEntity::test_set_playback_shuffle_update tests/test_device.py::TestDeviceEntity::test_set_repeat tests/test_device.py::TestDeviceEntity::test_set_repeat_update tests/test_device.py::TestDeviceEntity::test_set_tv_channel tests/test_device.py::TestDeviceEntity::test_set_tv_channel_update tests/test_device.py::TestDeviceEntity::test_channel_up tests/test_device.py::TestDeviceEntity::test_channel_down tests/test_device.py::TestDeviceStatus::test_volume_range tests/test_device.py::TestDeviceStatus::test_input_source tests/test_device.py::TestDeviceStatus::test_playback_repeat_mode
tests/test_device.py::TestDevice::test_init tests/test_device.py::TestDevice::test_apply_data tests/test_device.py::TestDevice::test_get_capability tests/test_device.py::TestDeviceEntity::test_refresh tests/test_device.py::TestDeviceEntity::test_save tests/test_device.py::TestDeviceEntity::test_switch_on tests/test_device.py::TestDeviceEntity::test_switch_on_update tests/test_device.py::TestDeviceEntity::test_switch_off tests/test_device.py::TestDeviceEntity::test_switch_off_update tests/test_device.py::TestDeviceEntity::test_lock tests/test_device.py::TestDeviceEntity::test_lock_update tests/test_device.py::TestDeviceEntity::test_unlock tests/test_device.py::TestDeviceEntity::test_unlock_update tests/test_device.py::TestDeviceEntity::test_set_level tests/test_device.py::TestDeviceEntity::test_set_level_invalid tests/test_device.py::TestDeviceEntity::test_set_level_update tests/test_device.py::TestDeviceEntity::test_set_fan_speed tests/test_device.py::TestDeviceEntity::test_set_fan_speed_invalid tests/test_device.py::TestDeviceEntity::test_set_fan_speed_update tests/test_device.py::TestDeviceEntity::test_status tests/test_device.py::TestDeviceEntity::test_set_color_temperature tests/test_device.py::TestDeviceEntity::test_set_color_temperature_invalid tests/test_device.py::TestDeviceEntity::test_set_color_temperature_update tests/test_device.py::TestDeviceEntity::test_set_hue tests/test_device.py::TestDeviceEntity::test_set_hue_invalid tests/test_device.py::TestDeviceEntity::test_set_hue_update tests/test_device.py::TestDeviceEntity::test_set_saturation tests/test_device.py::TestDeviceEntity::test_set_saturation_invalid tests/test_device.py::TestDeviceEntity::test_set_saturation_update tests/test_device.py::TestDeviceEntity::test_set_color tests/test_device.py::TestDeviceEntity::test_set_color_invalid tests/test_device.py::TestDeviceEntity::test_set_color_update tests/test_device.py::TestDeviceEntity::test_set_color_hex tests/test_device.py::TestDeviceEntity::test_set_color_hex_invalid tests/test_device.py::TestDeviceEntity::test_set_color_update_hex tests/test_device.py::TestDeviceEntity::test_set_thermostat_fan_mode_legacy tests/test_device.py::TestDeviceEntity::test_set_thermostat_fan_mode tests/test_device.py::TestDeviceEntity::test_set_thermostat_fan_mode_update tests/test_device.py::TestDeviceEntity::test_set_thermostat_mode_legacy tests/test_device.py::TestDeviceEntity::test_set_thermostat_mode tests/test_device.py::TestDeviceEntity::test_set_thermostat_mode_update tests/test_device.py::TestDeviceEntity::test_set_cooling_setpoint_legacy tests/test_device.py::TestDeviceEntity::test_set_cooling_setpoint_mode tests/test_device.py::TestDeviceEntity::test_set_cooling_setpoint_update tests/test_device.py::TestDeviceEntity::test_set_heating_setpoint_legacy tests/test_device.py::TestDeviceEntity::test_set_heating_setpoint_mode tests/test_device.py::TestDeviceEntity::test_set_heating_setpoint_update tests/test_device.py::TestDeviceEntity::test_open tests/test_device.py::TestDeviceEntity::test_open_legacy tests/test_device.py::TestDeviceEntity::test_close tests/test_device.py::TestDeviceEntity::test_close_legacy tests/test_device.py::TestDeviceEntity::test_open_window_shade tests/test_device.py::TestDeviceEntity::test_close_window_shade tests/test_device.py::TestDeviceEntity::test_request_drlc_action tests/test_device.py::TestDeviceEntity::test_override_drlc_action tests/test_device.py::TestDeviceEntity::test_execute tests/test_device.py::TestDeviceEntity::test_preset_position tests/test_device.py::TestDeviceEntity::test_set_air_conditioner_mode tests/test_device.py::TestDeviceEntity::test_set_fan_mode tests/test_device.py::TestDeviceEntity::test_set_air_flow_direction tests/test_device.py::TestDeviceStatus::test_init tests/test_device.py::TestDeviceStatus::test_apply_data tests/test_device.py::TestDeviceStatus::test_apply_attribute_update tests/test_device.py::TestDeviceStatus::test_apply_attribute_update_preserve_unit tests/test_device.py::TestDeviceStatus::test_apply_attribute_update_child_status tests/test_device.py::TestDeviceStatus::test_values tests/test_device.py::TestDeviceStatus::test_attributes tests/test_device.py::TestDeviceStatus::test_attributes_default tests/test_device.py::TestDeviceStatus::test_refresh tests/test_device.py::TestDeviceStatus::test_switch tests/test_device.py::TestDeviceStatus::test_level tests/test_device.py::TestDeviceStatus::test_level_range tests/test_device.py::TestDeviceStatus::test_fan_speed tests/test_device.py::TestDeviceStatus::test_fan_speed_range tests/test_device.py::TestDeviceStatus::test_hue_range tests/test_device.py::TestDeviceStatus::test_saturation_range tests/test_device.py::TestDeviceStatus::test_color_temperature_range tests/test_device.py::TestDeviceStatus::test_color_format tests/test_device.py::TestDeviceStatus::test_is_on tests/test_device.py::TestDeviceStatus::test_well_known_attributes tests/test_device.py::TestDeviceStatus::test_well_known_ocf_attributes tests/test_device.py::TestDeviceStatus::test_well_known_drlc_attributes tests/test_device.py::TestDeviceStatus::test_well_known_power_consumption_attributes
pysmartthings/capability.py pysmartthings/device.py
pysmartthings/capability.py pysmartthings/device.py