├── .github ├── FUNDING.yml └── workflows │ ├── automerge.yaml │ ├── hacs.yaml │ ├── hassfest.yaml │ └── release.yaml ├── LICENSE ├── README.md ├── custom_components └── smartthings_soundbar │ ├── __init__.py │ ├── api.py │ ├── manifest.json │ └── media_player.py └── hacs.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: piotrmachowski 2 | custom: ["buycoffee.to/piotrmachowski", "paypal.me/PiMachowski", "revolut.me/314ma"] 3 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yaml: -------------------------------------------------------------------------------- 1 | name: 'Automatically merge master -> dev' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Automatically merge master to dev 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | name: Git checkout 15 | with: 16 | fetch-depth: 0 17 | - name: Merge master -> dev 18 | run: | 19 | git config user.name "GitHub Actions" 20 | git config user.email "PiotrMachowski@users.noreply.github.com" 21 | if (git checkout dev) 22 | then 23 | git merge --ff-only master || git merge --no-commit master 24 | git commit -m "Automatically merge master -> dev" || echo "No commit needed" 25 | git push origin dev 26 | else 27 | echo "No dev branch" 28 | fi -------------------------------------------------------------------------------- /.github/workflows/hacs.yaml: -------------------------------------------------------------------------------- 1 | name: Validate HACS 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | name: Download repo 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: actions/setup-python@v2 15 | name: Setup Python 16 | with: 17 | python-version: '3.13.x' 18 | 19 | - uses: actions/cache@v2 20 | name: Cache 21 | with: 22 | path: | 23 | ~/.cache/pip 24 | key: custom-component-ci 25 | 26 | - name: HACS Action 27 | uses: hacs/action@main 28 | with: 29 | CATEGORY: integration 30 | ignore: brands 31 | -------------------------------------------------------------------------------- /.github/workflows/hassfest.yaml: -------------------------------------------------------------------------------- 1 | name: Validate with hassfest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | validate: 9 | runs-on: "ubuntu-latest" 10 | steps: 11 | - uses: "actions/checkout@v2" 12 | - uses: home-assistant/actions/hassfest@master 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | COMPONENT_NAME: smartthings_soundbar 9 | 10 | jobs: 11 | release: 12 | name: Prepare release 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | id-token: write 17 | steps: 18 | - name: Download repo 19 | uses: actions/checkout@v4.2.2 20 | 21 | - name: Adjust version number 22 | shell: bash 23 | run: | 24 | version="${{ github.event.release.tag_name }}" 25 | yq e -P -o=json \ 26 | -i ".version = \"${version}\"" \ 27 | "${{ github.workspace }}/custom_components/${{ env.COMPONENT_NAME }}/manifest.json" 28 | 29 | - name: Zip ${{ env.COMPONENT_NAME }} dir 30 | run: | 31 | cd "${{ github.workspace }}/custom_components/${{ env.COMPONENT_NAME }}" 32 | zip ${{ env.COMPONENT_NAME }}.zip -r ./ 33 | 34 | 35 | - name: Upload zip to release 36 | uses: softprops/action-gh-release@v2.1.0 37 | with: 38 | files: ${{ github.workspace }}/custom_components/${{ env.COMPONENT_NAME }}/${{ env.COMPONENT_NAME }}.zip 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Piotr Machowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![HACS Custom][hacs_shield]][hacs] 2 | [![GitHub Latest Release][releases_shield]][latest_release] 3 | [![GitHub All Releases][downloads_total_shield]][releases] 4 | [![Ko-Fi][ko_fi_shield]][ko_fi] 5 | [![buycoffee.to][buycoffee_to_shield]][buycoffee_to] 6 | [![PayPal.Me][paypal_me_shield]][paypal_me] 7 | [![Revolut.Me][revolut_me_shield]][revolut_me] 8 | 9 | 10 | 11 | [hacs_shield]: https://img.shields.io/static/v1.svg?label=HACS&message=Custom&style=popout&color=orange&labelColor=41bdf5&logo=HomeAssistantCommunityStore&logoColor=white 12 | [hacs]: https://hacs.xyz/docs/faq/custom_repositories 13 | 14 | [latest_release]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/releases/latest 15 | [releases_shield]: https://img.shields.io/github/release/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar.svg?style=popout 16 | 17 | [releases]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/releases 18 | [downloads_total_shield]: https://img.shields.io/github/downloads/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/total 19 | 20 | 21 | # SmartThings Soundbar 22 | 23 | Adds support for SmartThings enabled Soundbar 24 | 25 | ## Features 26 | 27 | - Turn on/off 28 | - Set volume 29 | - Step volume up/down 30 | - Mute/unmute 31 | - Select source 32 | - Show current volume level 33 | - Show current state: on/off/playing/paused/idle 34 | - Show if muted/unmuted 35 | - Show current source 36 | 37 | 38 | ## Configuration options 39 | 40 | | Key | Type | Required | Default | Description | 41 | | --- | --- | --- | --- | --- | 42 | | `name` | `string` | `False` | `SmartThings Soundbar` | Name of soundbar | 43 | | `api_key` | `string` | `True` | - | SmartThings API key (see: [here](https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar#getting-api-key-and-device-id)) | 44 | | `device_id` | `string` | `True` | - | SmartThings device id (see: [here](https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar#getting-api-key-and-device-id)) | 45 | | `max_volume` | `positive integer` | `False` | 100 | Volume level that will be used as a maximum level in Home Assistant | 46 | 47 | ## Example usage 48 | 49 | ```yaml 50 | media_player: 51 | - platform: smartthings_soundbar 52 | name: Soundbar 53 | api_key: "YOUR API KEY" 54 | device_id: "YOUR DEVICE ID" 55 | max_volume: 30 56 | ``` 57 | 58 | 59 | ## Getting API key and device id 60 | 61 | Make sure your device is connected to your SmartThings account. 62 | 63 | Obtain an API key by following [these steps](https://www.home-assistant.io/integrations/smartthings/#personal-access-token-pat). 64 | 65 | Once you're signed in on that page, go to https://api.smartthings.com/v1/devices to see a list of your devices and their device IDs. 66 | 67 | ## Installation 68 | 69 | ### Using [HACS](https://hacs.xyz/) (recommended) 70 | 71 | This integration can be added to HACS as a [custom repository](https://hacs.xyz/docs/faq/custom_repositories): 72 | * URL: `https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar` 73 | * Category: `Integration` 74 | 75 | After adding a custom repository you can use HACS to install this integration using user interface. 76 | 77 | ### Manual 78 | 79 | To install this integration manually you have to download [*smartthings_soundbar.zip*](https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/releases/latest/download/smartthings_soundbar.zip) and extract its contents to `config/custom_components/smartthings_soundbar` directory: 80 | ```bash 81 | mkdir -p custom_components/smartthings_soundbar 82 | cd custom_components/smartthings_soundbar 83 | wget https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/releases/latest/download/smartthings_soundbar.zip 84 | unzip smartthings_soundbar.zip 85 | rm smartthings_soundbar.zip 86 | ``` 87 | 88 | ### Known problems 89 | 90 | * If you have config validation issues after installing this component you have to follow these steps: 91 | * Install custom component 92 | * Restart Home Assistant 93 | * Add configuration 94 | * Restart Home Assistant again 95 | 96 | ## Supported devices 97 | 98 | This integration was confirmed to work with following devices: 99 | 100 | - Samsung HW-N950 101 | - Samsung HW-Q800B 102 | - Samsung HW-Q800T 103 | - Samsung HW-Q950T 104 | - Samsung HW-Q990B 105 | - Samsung HW-Q990C 106 | - Samsung HW-Q90R 107 | - Samsung HW-Q80R 108 | - Samsung HW-Q70R 109 | - Samsung HW-S60T 110 | - Samsung HW-S61T 111 | 112 | 113 | 114 | 115 | 116 | ## Support 117 | 118 | If you want to support my work with a donation you can use one of the following platforms: 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 133 | 136 | 140 | 141 | 142 | 143 | 147 | 150 | 151 | 152 | 153 | 154 | 157 | 160 | 163 | 164 | 165 | 166 | 170 | 173 | 176 | 177 |
PlatformPayment methodsLinkComment
Ko-fi 130 |
  • PayPal
  • 131 |
  • Credit card
  • 132 |
    134 | Buy Me a Coffee at ko-fi.com 135 | 137 |
  • No fees
  • 138 |
  • Single or monthly payment
  • 139 |
    buycoffee.to 144 |
  • BLIK
  • 145 |
  • Bank transfer
  • 146 |
    148 | Postaw mi kawę na buycoffee.to 149 |
    PayPal 155 |
  • PayPal
  • 156 |
    158 | PayPal Logo 159 | 161 |
  • No fees
  • 162 |
    Revolut 167 |
  • Revolut
  • 168 |
  • Credit Card
  • 169 |
    171 | Revolut 172 | 174 |
  • No fees
  • 175 |
    178 | 179 | ### Powered by 180 | [![PyCharm logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 181 | 182 | 183 | [ko_fi_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Ko-Fi&color=F16061&logo=ko-fi&logoColor=white 184 | 185 | [ko_fi]: https://ko-fi.com/piotrmachowski 186 | 187 | [buycoffee_to_shield]: https://shields.io/badge/buycoffee.to-white?style=flat&labelColor=white&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhmlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVh1YQcchQnayIijhKFYtgobQVWnUweemP0KQhSXFxFFwLDv4sVh1cnHV1cBUEwR8QVxcnRRcp8b6k0CLGC4/3cd49h/fuA4R6malmxzigapaRisfEbG5FDLzChxB6MIZ+iZl6Ir2QgWd93VM31V2UZ3n3/Vm9St5kgE8knmW6YRGvE09vWjrnfeIwK0kK8TnxqEEXJH7kuuzyG+eiwwLPDBuZ1BxxmFgstrHcxqxkqMRTxBFF1ShfyLqscN7irJarrHlP/sJgXltOc53WEOJYRAJJiJBRxQbKsBClXSPFRIrOYx7+QcefJJdMrg0wcsyjAhWS4wf/g9+zNQuTE25SMAZ0vtj2xzAQ2AUaNdv+PrbtxgngfwautJa/UgdmPkmvtbTIEdC3DVxctzR5D7jcAQaedMmQHMlPSygUgPcz+qYcELoFulfduTXPcfoAZGhWSzfAwSEwUqTsNY93d7XP7d+e5vx+AIahcq//o+yoAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5wETCy4vFNqLzwAAAVpJREFUOMvd0rFLVXEYxvHPOedKJnKJhrDLuUFREULE7YDCMYj+AydpsCWiaKu29hZxiP4Al4aWwC1EdFI4Q3hqEmkIBI8ZChWXKNLLvS0/Qcza84V3enm/7/s878t/HxGkeTaIGziP+EB918nawu7Dq1d0e1+2J2bepnk2jFEUVVF+qKV51o9neBCaugfge70keoxxUbSWjrQ+4SUyzKZ5NlnDZdzGG7w4DIh+dtZEFntDA98l8S0MYwctNGrYz9WqKJePFLq80g5Sr+EHlnATp+NA+4qLaZ7FfzMrzbMBjGEdq8GrJMZnvAvFC/8wfAwjWMQ8XmMzaW9sdevNRgd3MFhvNpbaG1u/Dk2/hOc4gadVUa7Um425qii/7Z+xH9O4jwW8Cqv24Tru4hyeVEU588cfBMgpPMI9nMFe0BkFzVOYrYqycyQgQJLwTC2cDZCPeF8V5Y7jGb8BUpRicy7OU5MAAAAASUVORK5CYII= 188 | 189 | [buycoffee_to]: https://buycoffee.to/piotrmachowski 190 | 191 | [buy_me_a_coffee_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=6f4e37&logo=buy%20me%20a%20coffee&logoColor=white 192 | 193 | [buy_me_a_coffee]: https://www.buymeacoffee.com/PiotrMachowski 194 | 195 | [paypal_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal 196 | 197 | [paypal_me]: https://paypal.me/PiMachowski 198 | 199 | [revolut_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Revolut&logo=revolut 200 | 201 | [revolut_me]: https://revolut.me/314ma 202 | 203 | -------------------------------------------------------------------------------- /custom_components/smartthings_soundbar/__init__.py: -------------------------------------------------------------------------------- 1 | """SmartThings Soundbar""" -------------------------------------------------------------------------------- /custom_components/smartthings_soundbar/api.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | from homeassistant.const import (STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING, STATE_UNAVAILABLE) 5 | 6 | API_BASEURL = "https://api.smartthings.com/v1" 7 | API_DEVICES = API_BASEURL + "/devices/" 8 | COMMAND_POWER_ON = "{'commands': [{'component': 'main','capability': 'switch','command': 'on'}]}" 9 | COMMAND_POWER_OFF = "{'commands': [{'component': 'main','capability': 'switch','command': 'off'}]}" 10 | COMMAND_REFRESH = "{'commands':[{'component': 'main','capability': 'refresh','command': 'refresh'}]}" 11 | COMMAND_PAUSE = "{'commands':[{'component': 'main','capability': 'mediaPlayback','command': 'pause'}]}" 12 | COMMAND_MUTE = "{'commands':[{'component': 'main','capability': 'audioMute','command': 'mute'}]}" 13 | COMMAND_UNMUTE = "{'commands':[{'component': 'main','capability': 'audioMute','command': 'unmute'}]}" 14 | COMMAND_PLAY = "{'commands':[{'component': 'main','capability': 'mediaPlayback','command': 'play'}]}" 15 | COMMAND_STOP = "{'commands':[{'component': 'main','capability': 'mediaPlayback','command': 'stop'}]}" 16 | COMMAND_REWIND = "{'commands':[{'component': 'main','capability': 'mediaPlayback','command': 'rewind'}]}" 17 | COMMAND_FAST_FORWARD = "{'commands':[{'component': 'main','capability': 'mediaPlayback','command': 'fastForward'}]}" 18 | 19 | CONTROLLABLE_SOURCES = ["bluetooth", "wifi"] 20 | 21 | 22 | class SoundbarApi: 23 | 24 | @staticmethod 25 | def device_update(entity): 26 | API_KEY = entity._api_key 27 | REQUEST_HEADERS = {"Authorization": "Bearer " + API_KEY} 28 | DEVICE_ID = entity._device_id 29 | API_DEVICE = API_DEVICES + DEVICE_ID 30 | API_DEVICE_STATUS = API_DEVICE + "/states" 31 | API_COMMAND = API_DEVICE + "/commands" 32 | cmdurl = requests.post(API_COMMAND, data=COMMAND_REFRESH, headers=REQUEST_HEADERS) 33 | resp = requests.get(API_DEVICE_STATUS, headers=REQUEST_HEADERS) 34 | data = resp.json() 35 | 36 | switch_state = SoundbarApi.extractor(data, "main.switch.value") 37 | if switch_state is None: 38 | entity._state = STATE_UNAVAILABLE 39 | return 40 | playback_state = SoundbarApi.extractor(data, "main.playbackStatus.value") 41 | device_source = SoundbarApi.extractor(data, "main.inputSource.value") 42 | device_all_sources = json.loads(SoundbarApi.extractor(data, "main.supportedInputSources.value")) 43 | device_muted = SoundbarApi.extractor(data, "main.mute.value") != "unmuted" 44 | device_volume = SoundbarApi.extractor(data, "main.volume.value") 45 | device_volume = min(int(device_volume) / entity._max_volume, 1) 46 | 47 | if switch_state == "on": 48 | if device_source.lower() in CONTROLLABLE_SOURCES: 49 | if playback_state == "playing": 50 | entity._state = STATE_PLAYING 51 | elif playback_state == "paused": 52 | entity._state = STATE_PAUSED 53 | else: 54 | entity._state = STATE_ON 55 | else: 56 | entity._state = STATE_ON 57 | else: 58 | entity._state = STATE_OFF 59 | entity._volume = device_volume 60 | entity._source_list = device_all_sources if type(device_all_sources) is list else device_all_sources["value"] 61 | entity._muted = device_muted 62 | entity._source = device_source 63 | if entity._state in [STATE_PLAYING, STATE_PAUSED] and 'trackDescription' in data['main']: 64 | entity._media_title = SoundbarApi.extractor(data, "main.trackDescription.value") 65 | else: 66 | entity._media_title = None 67 | 68 | @staticmethod 69 | def send_command(entity, argument, cmdtype): 70 | API_KEY = entity._api_key 71 | REQUEST_HEADERS = {"Authorization": "Bearer " + API_KEY} 72 | DEVICE_ID = entity._device_id 73 | API_DEVICES = API_BASEURL + "/devices/" 74 | API_DEVICE = API_DEVICES + DEVICE_ID 75 | API_COMMAND = API_DEVICE + "/commands" 76 | 77 | if cmdtype == "setvolume": # sets volume 78 | API_COMMAND_DATA = "{'commands':[{'component': 'main','capability': 'audioVolume','command': 'setVolume','arguments': " 79 | volume = int(argument * entity._max_volume) 80 | API_COMMAND_ARG = "[{}]}}]}}".format(volume) 81 | API_FULL = API_COMMAND_DATA + API_COMMAND_ARG 82 | cmdurl = requests.post(API_COMMAND, data=API_FULL, headers=REQUEST_HEADERS) 83 | elif cmdtype == "stepvolume": # steps volume up or down 84 | if argument == "up": 85 | API_COMMAND_DATA = "{'commands':[{'component': 'main','capability': 'audioVolume','command': 'volumeUp'}]}" 86 | cmdurl = requests.post(API_COMMAND, data=API_COMMAND_DATA, headers=REQUEST_HEADERS) 87 | else: 88 | API_COMMAND_DATA = "{'commands':[{'component': 'main','capability': 'audioVolume','command': 'volumeDown'}]}" 89 | cmdurl = requests.post(API_COMMAND, data=API_COMMAND_DATA, headers=REQUEST_HEADERS) 90 | elif cmdtype == "audiomute": # mutes audio 91 | if entity._muted == False: 92 | cmdurl = requests.post(API_COMMAND, data=COMMAND_MUTE, headers=REQUEST_HEADERS) 93 | else: 94 | cmdurl = requests.post(API_COMMAND, data=COMMAND_UNMUTE, headers=REQUEST_HEADERS) 95 | elif cmdtype == "switch_off": # turns off 96 | cmdurl = requests.post(API_COMMAND, data=COMMAND_POWER_OFF, headers=REQUEST_HEADERS) 97 | elif cmdtype == "switch_on": # turns on 98 | cmdurl = requests.post(API_COMMAND, data=COMMAND_POWER_ON, headers=REQUEST_HEADERS) 99 | elif cmdtype == "play": # play 100 | cmdurl = requests.post(API_COMMAND, data=COMMAND_PLAY, headers=REQUEST_HEADERS) 101 | elif cmdtype == "pause": # pause 102 | cmdurl = requests.post(API_COMMAND, data=COMMAND_PAUSE, headers=REQUEST_HEADERS) 103 | elif cmdtype == "selectsource": # changes source 104 | API_COMMAND_DATA = "{'commands':[{'component': 'main','capability': 'mediaInputSource','command': 'setInputSource', 'arguments': " 105 | API_COMMAND_ARG = "['{}']}}]}}".format(argument) 106 | API_FULL = API_COMMAND_DATA + API_COMMAND_ARG 107 | cmdurl = requests.post(API_COMMAND, data=API_FULL, headers=REQUEST_HEADERS) 108 | elif cmdtype == "selectsoundmode": 109 | API_COMMAND_DATA = f"""{{ 110 | "commands":[ 111 | {{ 112 | "component":"main", 113 | "capability":"execute", 114 | "command":"execute", 115 | "arguments":[ 116 | "/sec/networkaudio/soundmode", 117 | {{ 118 | "x.com.samsung.networkaudio.soundmode":"{argument}" 119 | }} 120 | ] 121 | }} 122 | ] 123 | }}""" 124 | cmdurl = requests.post(API_COMMAND, data=API_COMMAND_DATA, headers=REQUEST_HEADERS) 125 | entity.schedule_update_ha_state() 126 | 127 | @staticmethod 128 | def extractor(json, path): 129 | def extractor_arr(json_obj, path_array): 130 | if path_array[0] not in json_obj: 131 | return None 132 | if len(path_array) > 1: 133 | return extractor_arr(json_obj[path_array[0]], path_array[1:]) 134 | return json_obj[path_array[0]] 135 | try: 136 | return extractor_arr(json, path.split(".")) 137 | except: 138 | return None -------------------------------------------------------------------------------- /custom_components/smartthings_soundbar/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "smartthings_soundbar", 3 | "name": "SmartThings Soundbar", 4 | "codeowners": [ 5 | "@PiotrMachowski" 6 | ], 7 | "dependencies": [], 8 | "documentation": "https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar", 9 | "iot_class": "cloud_polling", 10 | "issue_tracker": "https://github.com/PiotrMachowski/Home-Assistant-custom-components-SmartThings-Soundbar/issues", 11 | "requirements": [], 12 | "version": "v0.0.0" 13 | } 14 | -------------------------------------------------------------------------------- /custom_components/smartthings_soundbar/media_player.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import voluptuous as vol 3 | 4 | from .api import SoundbarApi 5 | 6 | from homeassistant.components.media_player import ( 7 | MediaPlayerEntity, 8 | MediaPlayerEntityFeature, 9 | MediaPlayerDeviceClass, 10 | PLATFORM_SCHEMA, 11 | ) 12 | from homeassistant.const import ( 13 | CONF_NAME, CONF_API_KEY, CONF_DEVICE_ID 14 | ) 15 | import homeassistant.helpers.config_validation as cv 16 | 17 | _LOGGER = logging.getLogger(__name__) 18 | 19 | DEFAULT_NAME = "SmartThings Soundbar" 20 | CONF_MAX_VOLUME = "max_volume" 21 | 22 | SUPPORT_SMARTTHINGS_SOUNDBAR = ( 23 | MediaPlayerEntityFeature.PAUSE 24 | | MediaPlayerEntityFeature.VOLUME_STEP 25 | | MediaPlayerEntityFeature.VOLUME_MUTE 26 | | MediaPlayerEntityFeature.VOLUME_SET 27 | | MediaPlayerEntityFeature.SELECT_SOURCE 28 | | MediaPlayerEntityFeature.TURN_OFF 29 | | MediaPlayerEntityFeature.TURN_ON 30 | | MediaPlayerEntityFeature.PLAY 31 | | MediaPlayerEntityFeature.SELECT_SOUND_MODE 32 | ) 33 | 34 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( 35 | { 36 | vol.Required(CONF_API_KEY): cv.string, 37 | vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, 38 | vol.Optional(CONF_DEVICE_ID): cv.string, 39 | vol.Optional(CONF_MAX_VOLUME, default=100): cv.positive_int, 40 | } 41 | ) 42 | 43 | 44 | def setup_platform(hass, config, add_entities, discovery_info=None): 45 | name = config.get(CONF_NAME) 46 | api_key = config.get(CONF_API_KEY) 47 | device_id = config.get(CONF_DEVICE_ID) 48 | max_volume = config.get(CONF_MAX_VOLUME) 49 | add_entities([SmartThingsSoundbarMediaPlayer(name, api_key, device_id, max_volume)]) 50 | 51 | 52 | class SmartThingsSoundbarMediaPlayer(MediaPlayerEntity): 53 | 54 | def __init__(self, name, api_key, device_id, max_volume): 55 | self._name = name 56 | self._device_id = device_id 57 | self._api_key = api_key 58 | self._max_volume = max_volume 59 | self._volume = 1 60 | self._muted = False 61 | self._playing = True 62 | self._state = "on" 63 | self._source = "" 64 | self._source_list = [] 65 | self._media_title = "" 66 | 67 | def update(self): 68 | SoundbarApi.device_update(self) 69 | 70 | @property 71 | def unique_id(self) -> str | None: 72 | return f"SmartThings_Soundbar_{self._device_id}" 73 | 74 | def turn_off(self): 75 | arg = "" 76 | cmdtype = "switch_off" 77 | SoundbarApi.send_command(self, arg, cmdtype) 78 | 79 | def turn_on(self): 80 | arg = "" 81 | cmdtype = "switch_on" 82 | SoundbarApi.send_command(self, arg, cmdtype) 83 | 84 | def set_volume_level(self, arg, cmdtype="setvolume"): 85 | SoundbarApi.send_command(self, arg, cmdtype) 86 | 87 | def mute_volume(self, mute, cmdtype="audiomute"): 88 | SoundbarApi.send_command(self, mute, cmdtype) 89 | 90 | def volume_up(self, cmdtype="stepvolume"): 91 | arg = "up" 92 | SoundbarApi.send_command(self, arg, cmdtype) 93 | 94 | def volume_down(self, cmdtype="stepvolume"): 95 | arg = "" 96 | SoundbarApi.send_command(self, arg, cmdtype) 97 | 98 | def select_source(self, source, cmdtype="selectsource"): 99 | SoundbarApi.send_command(self, source, cmdtype) 100 | 101 | def select_sound_mode(self, sound_mode): 102 | SoundbarApi.send_command(self, sound_mode, "selectsoundmode") 103 | 104 | @property 105 | def device_class(self): 106 | return MediaPlayerDeviceClass.SPEAKER 107 | 108 | @property 109 | def supported_features(self): 110 | return SUPPORT_SMARTTHINGS_SOUNDBAR 111 | 112 | @property 113 | def name(self): 114 | return self._name 115 | 116 | @property 117 | def media_title(self): 118 | return self._media_title 119 | 120 | def media_play(self): 121 | arg = "" 122 | cmdtype = "play" 123 | SoundbarApi.send_command(self, arg, cmdtype) 124 | 125 | def media_pause(self): 126 | arg = "" 127 | cmdtype = "pause" 128 | SoundbarApi.send_command(self, arg, cmdtype) 129 | 130 | @property 131 | def state(self): 132 | return self._state 133 | 134 | @property 135 | def is_volume_muted(self): 136 | return self._muted 137 | 138 | @property 139 | def volume_level(self): 140 | return self._volume 141 | 142 | @property 143 | def source(self): 144 | return self._source 145 | 146 | @property 147 | def source_list(self): 148 | return self._source_list 149 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SmartThings Soundbar", 3 | "render_readme": true, 4 | "zip_release": true, 5 | "filename": "smartthings_soundbar.zip" 6 | } --------------------------------------------------------------------------------