diff --git a/README.md b/README.md index 20788ec..2853a17 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,15 @@ - Fully functional touchpad for navigation (thanks to [iablon's Touchpad Card](https://github.com/iablon/HomeAssistant-Touchpad-Card)) ❤️ - Slider for volume (thanks to [AnthonMS's Slider Card](https://github.com/AnthonMS/my-cards#slider-card)) ❤️ -- Makes use of [ollo69's SamsungTV Smart Component](https://github.com/ollo69/ha-samsungtv-smart) +- Supports [ollo69's SamsungTV Smart Component](https://github.com/ollo69/ha-samsungtv-smart) - Much easier setup - Implements haptics feedback - Customizable layout, you can choose the order of the rows and buttons -- All rows and buttons are optional - -## Notice - -⚠️ **Currently this fork probably works only with Samsung Smart TVs (Tizen)**, since i made it first for my personal use. +- All rows and buttons are optional, you can change whatever you *(don't)* like ## Demo -ex +ex ## Options @@ -38,6 +34,8 @@ | enable_button_feedback | boolean | **Optional** | Shall clicks on the buttons return a vibration feedback? Defaults to `true`. | enable_slider_feedback | boolean | **Optional** | Shall the volume slider return a vibration feedback when you slide through it? Defaults to `true`. | slider_config | object | **Optional** | Custom configuration for the volume slider. See [slider-card](https://github.com/AnthonMS/my-cards) +| custom_keys | object | **Optional** | Custom keys for the remote control. Each item is an object that should have `icon` and at least one of the following properties: `key`, `source`, `service`. +| custom_sources | object | **Optional** | Custom sources for the remote control. Same object as above, but letting you split keys and sources. Using only these options you will get an empty card (or almost empty, if you set a title). In order to include the buttons, you need to specify in the config the rows you want and which buttons you want in it. @@ -61,6 +59,99 @@ There also `volume_row` and `navigation_row`, but these requires a string as val | volume_row | string | Can be either `slider` or `buttons`. This defines the mode you want for setting the volume (you'll see them soon below). | navigation_row | string | Can be either `touchpad` or `buttons`. This defines the mode you want for navigating around your tv (you'll also see them soon below). +## **Notice** + +This card uses `media_player.play_media` to send keys to the TV. +This is the way [ollo69's SamsungTV Smart Component](https://github.com/ollo69/ha-samsungtv-smart) (which i based this card on) works, but don't worry: if your TV is from another brand or simply the TV integration does not use `media_player.play_media` for sending keys, you can still use this card by setting [custom buttons](#custom-buttons) with services to send keys to your TV (or do whatever you want) in your way (just like the original [tv-card](https://github.com/marrobHD/tv-card)). + +## Custom buttons + +If you want to add custom buttons to the remote control (of if you want to reconfigure the existing buttons), you can do it by adding an object to the `custom_keys` option: + +```yaml +custom_keys: + input_tv: + icon: "mdiTelevisionBox" + key: KEY_TV + browser: + icon: "mdiWeb" + source: "browser" + toggle_light: + icon: "mdiLightbulb" + service: light.toggle + service_data: + entity_id: light.bedroom +``` + +The `custom_sources` exists for the same purpose, but you can use it to split the keys and sources. + +```yaml +custom_keys: + input_tv: + icon: "mdiTelevisionBox" + key: KEY_TV + toggle_light: + icon: "mdiLightbulb" + service: light.toggle + service_data: + entity_id: light.bedroom +custom_sources: + browser: + icon: "mdiWeb" + source: "browser" +``` + +Then you can easily use these buttons in your card: + +```yaml +power_row: + - browser + - power + - input_tv +media_contol_row: + - rewind + - play + - pause + - fast_forward + - toggle_light +``` + +guide + +With custom buttons you can override existing buttons for changing its icon or even its functionality. Here i do both: + +```yaml +custom_keys: + power: + icon: "mdiPowerCycle" + service: media_player.toggle + service_data: + entity_id: media_player.tv +``` + +Inside each button you may define either `key`, `source` or `service`, as you've seen. + +| Option | internal function | Description +| ---- | ---- | ------- +| key | `media_player.play_media(media_content_id=key, media_content_type="send_key")` | The key to send to the TV via `media_player.play_media` +| source | `media_player.select_source(source=source)` | The source to switch to via `media_player.select_source` +| service | `_hass.callService(domain, service, service_data)` | A string representing service to call. Use the format `domain.service`, e.g. `"light.turn_on"` +| service_data | passed with `service` | The data to pass to the service. May be an object depending on the service you are using. + +In the option `icon` you must pass the name of the javascript object that represents the icon using MDI. You can get those [here](https://materialdesignicons.com/). +Searching for "guide", you'll find `television-guide`: + +guide + +See that pretty line of code in there? There's the name of the icon we want: `mdiTelevisionGuide` + +```yaml +custom_keys: + guide: + icon: "mdiTelevisionGuide" + key: KEY_GUIDE +``` + ## Installation ### Step 1 diff --git a/card_screenshot.png b/card_screenshot.png deleted file mode 100644 index d8a8f6d..0000000 Binary files a/card_screenshot.png and /dev/null differ diff --git a/custom_keys.png b/custom_keys.png new file mode 100644 index 0000000..9c37bfc Binary files /dev/null and b/custom_keys.png differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..8f74035 Binary files /dev/null and b/screenshot.png differ diff --git a/television_guide.png b/television_guide.png new file mode 100644 index 0000000..ee515c8 Binary files /dev/null and b/television_guide.png differ diff --git a/tv-card.js b/tv-card.js index 118a2fd..eadfce1 100644 --- a/tv-card.js +++ b/tv-card.js @@ -3,60 +3,39 @@ const LitElement = Object.getPrototypeOf( ); const html = LitElement.prototype.html; -import { - mdiPower, - mdiChevronUp, - mdiChevronLeft, - mdiCheckboxBlankCircle, - mdiChevronRight, - mdiChevronDown, - mdiArrowLeft, - mdiArrowRight, - mdiVideoInputHdmi, - mdiHome, - mdiArrowUp, - mdiTelevisionGuide, - mdiArrowDown, - mdiRewind, - mdiPlay, - mdiPause, - mdiFastForward, - mdiVolumeMute, - mdiVolumeMinus, - mdiVolumePlus, - mdiNetflix, - mdiYoutube, - mdiSpotify, -} from "https://unpkg.com/@mdi/js@6.4.95/mdi.js?module"; - -const custom_keys = { - "power": ["KEY_POWER", mdiPower], - "volume_up": ["KEY_VOLUP", mdiVolumePlus], - "volume_down": ["KEY_VOLDOWN", mdiVolumeMinus], - "volume_mute": ["KEY_MUTE", mdiVolumeMute], - "return": ["KEY_RETURN", mdiArrowLeft], - "source": ["KEY_SOURCE", mdiVideoInputHdmi], - "info": ["KEY_INFO", mdiTelevisionGuide], - "home": ["KEY_HOME", mdiHome], - "channel_up": ["KEY_CHUP", mdiArrowUp], - "channel_down": ["KEY_CHDOWN", mdiArrowDown], - "up": ["KEY_UP", mdiChevronUp], - "left": ["KEY_LEFT", mdiChevronLeft], - "enter": ["KEY_ENTER", mdiCheckboxBlankCircle], - "right": ["KEY_RIGHT", mdiChevronRight], - "down": ["KEY_DOWN", mdiChevronDown], - "rewind": ["KEY_REWIND", mdiRewind], - "play": ["KEY_PLAY", mdiPlay], - "pause": ["KEY_PAUSE", mdiPause], - "fast_forward": ["KEY_FF", mdiFastForward], +import * as mdiIcons from "https://unpkg.com/@mdi/js@6.4.95/mdi.js?module"; + +const keys = { + "power": {"key": "KEY_POWER", "icon": "mdiPower"}, + "volume_up": {"key": "KEY_VOLUP", "icon": "mdiVolumePlus"}, + "volume_down": {"key": "KEY_VOLDOWN", "icon": "mdiVolumeMinus"}, + "volume_mute": {"key": "KEY_MUTE", "icon": "mdiVolumeMute"}, + "return": {"key": "KEY_RETURN", "icon": "mdiArrowLeft"}, + "source": {"key": "KEY_SOURCE", "icon": "mdiVideoInputHdmi"}, + "info": {"key": "KEY_INFO", "icon": "mdiTelevisionGuide"}, + "home": {"key": "KEY_HOME", "icon": "mdiHome"}, + "channel_up": {"key": "KEY_CHUP", "icon": "mdiArrowUp"}, + "channel_down": {"key": "KEY_CHDOWN", "icon": "mdiArrowDown"}, + "up": {"key": "KEY_UP", "icon": "mdiChevronUp"}, + "left": {"key": "KEY_LEFT", "icon": "mdiChevronLeft"}, + "enter": {"key": "KEY_ENTER", "icon": "mdiCheckboxBlankCircle"}, + "right": {"key": "KEY_RIGHT", "icon": "mdiChevronRight"}, + "down": {"key": "KEY_DOWN", "icon": "mdiChevronDown"}, + "rewind": {"key": "KEY_REWIND", "icon": "mdiRewind"}, + "play": {"key": "KEY_PLAY", "icon": "mdiPlay"}, + "pause": {"key": "KEY_PAUSE", "icon": "mdiPause"}, + "fast_forward": {"key": "KEY_FF", "icon": "mdiFastForward"}, }; -const custom_sources = { - "netflix": ["Netflix", mdiNetflix], - "spotify": ["Spotify", mdiSpotify], - "youtube": ["YouTube", mdiYoutube], +const sources = { + "netflix": {"source": "Netflix", "icon": "mdiNetflix"}, + "spotify": {"source": "Spotify", "icon": "mdiSpotify"}, + "youtube": {"source": "YouTube", "icon": "mdiYoutube"}, }; +var custom_keys = {}; +var custom_sources = {}; + var fireEvent = function(node, type, detail, options) { options = options || {}; detail = detail === null || detail === undefined ? {} : detail; @@ -97,6 +76,8 @@ class TVCardServices extends LitElement { } this._config = { theme: "default", ...config }; + custom_keys = config.custom_keys || {}; + custom_sources = config.custom_sources || {}; this.loadCardHelpers(); this.renderVolumeSlider(); @@ -256,30 +237,32 @@ class TVCardServices extends LitElement { handleActionClick(e) { let action = e.currentTarget.action; + let info = custom_keys[action] || custom_sources[action] || keys[action] || sources[action]; - if (custom_keys[action]) { - this.sendKey(custom_keys[action][0]); - } else if (custom_sources[action]) { - this.changeSource(custom_sources[action][0]); + if (info.key) { + this.sendKey(info.key); + } + else if (info.source) { + this.changeSource(info.source); + } + else if (info.service) { + const [domain, service] = info.service.split(".", 2); + this._hass.callService(domain, service, info.service_data); } if (this._config.enable_button_feedback === undefined || this._config.enable_button_feedback) fireEvent(window, "haptic", "light"); } buildIconButton(action) { - let icon = ""; - if (custom_keys.hasOwnProperty(action)) { - icon = custom_keys[action][1]; - } else if (custom_sources.hasOwnProperty(action)) { - icon = custom_sources[action][1]; - } + let info = custom_keys[action] || custom_sources[action] || keys[action] || sources[action]; + let icon = info? info.icon : ""; return html ` `; }