const HAFiremoteVersion = 'v4.1.9'; import {LitElement, html, css, unsafeHTML, unsafeCSS, styleMap} from './lit/lit-all.min.js'; import {launcherData, launcherCSS} from "./launcher-buttons.js?version=v4.1.9"; import {rosettaStone} from './language-translations.js?version=v4.1.9'; import {devices} from './supported-devices.js?version=v4.1.9'; console.groupCollapsed("%c 🔥 FIREMOTE-CARD 🔥 %c "+HAFiremoteVersion+" installed ", "color: orange; font-weight: bold; background: black", "color: green; font-weight: bold;"), console.log("Readme:", "https://github.com/PRProd/HA-Firemote"), console.groupEnd(); const fireEvent = (node, type, detail, options) => { options = options || {}; detail = detail === null || detail === undefined ? {} : detail; const event = new Event(type, { bubbles: options.bubbles === undefined ? true : options.bubbles, cancelable: Boolean(options.cancelable), composed: options.composed === undefined ? true : options.composed }); event.detail = detail; node.dispatchEvent(event); return event; } // Process the imported data const devicemap = new Map(Object.entries(devices)); var appmap = new Map(Object.entries(launcherData)); const translationmap = new Map(Object.entries(rosettaStone)); // Set the max number of app launcher buttons for each remote style const appButtonMax = { "AF4": 6, "AF5": 6, "AF6": 6, "AFJTV": 6, "AFXF2": 6, "AR1": 10, "AR2": 8, "AR3": 8, "CC1": 8, "CC2": 8, "CC3": 8, "NS2": 6, "ON1": 8, "ON2": 8, "RVRP": 10, "RHR": 10, "RTR": 8, "RWR": 10, "RVR": 10, "RSR": 10, "XM1": 10, "XM2": 10, "HO1": 6, "HO2": 8, "HO3": 6, "HO4": 6, "AL1": appmap.size, "AL2": appmap.size,}; function deviceAttributeQuery(deviceAttribute, configvar){ var deviceTypeRef = configvar.device_type; if(configvar[deviceAttribute+'_override']) { if(configvar[deviceAttribute+'_override'] != 'none') { return configvar[deviceAttribute+'_override']; } } var attributeValue = ''; var deviceSearch = function(deviceName, jsonData) { for (var key in jsonData) { if(typeof(jsonData[key]) === 'object') { if(key == deviceName) { attributeValue = String(jsonData[key][deviceAttribute]); } else { deviceSearch(deviceName, jsonData[key]); } } } return attributeValue; } return String(deviceSearch(deviceTypeRef, devices)); } function truncate(str, length) { return str.length > length ? str.substr(0, length) : str; } function resetAppMap(){ appmap = new Map(Object.entries(launcherData)); } function handlehdmi(config, inputs = 0) { if( inputs > 0 ) { for (let i = 1; i <= inputs; i++) { var configitem = "hdmi_"+i; var inputname = "HDMI "+i; var friendlyname = "HDMI "+i; if (config[configitem]) { inputname = truncate(config[configitem], 8); friendlyname = 'HDMI '+i+' - '+inputname; } if( config.device_type == 'fire_tv_cube_third_gen') { appmap.set(configitem, {"button": inputname, "friendlyName": friendlyname, "androidName": "", "adbLaunchCommand": "adb shell am start -n com.amazon.tv.inputpreference.service/com.amazon.tv.inputpreference.player.InputChooserActivity"}); return; } if (config.device_family == "roku") { appmap.set(configitem, {"button": inputname, "friendlyName": friendlyname, "appName": "HDMI "+i, "remoteCommand": '{"command": "input_hdmi'+i+'", "num_repeats": 1, "hold_secs": 0}'}); } else { appmap.set(configitem, {"button": inputname, "friendlyName": friendlyname, "androidName": "", "adbLaunchCommand": "HDMI"+i}); } } } } function handlecustomlaunchers(config) { // Experimental - Only one of them works through the roku api for some reason /// // if (config.device_family == "roku") { // appmap.set("roku-secret-screen", {"button": "Secret", "friendlyName": "Function: Secret Screen 1"}); // } let customlaunchers = config.custom_launchers; if(typeof customlaunchers == 'undefined' || customlaunchers == null) { return; } if(customlaunchers.constructor !== Array) { customlaunchers = new Array(config.custom_launchers); } var l = 1; customlaunchers.forEach((launcher) => { var style = ''; var icon = null; var imagePath = null; if (launcher.color) { style = 'color:'+launcher.color+';'; } if (launcher.background) { style = style+'background:'+launcher.background+';'; } var friendlyname = launcher.friendly_name || launcher.label || "customlauncher "+l; var label = launcher.label || launcher.friendly_name || "customlauncher "+l; if (launcher.icon) { icon = '
'; } if (launcher.image_path) { imagePath = '
'+label+''; } label = '
'+truncate(label, 10)+'
'; var buttonFace = imagePath || icon || label appmap.set("customlauncher "+friendlyname, {"button": buttonFace, "friendlyName": "Custom: "+friendlyname, "script": launcher.script, "data": launcher.data}); l++; }) } function handleAVInputs(config, avInputs = 0) { if (avInputs > 0 && config.device_family == "roku") { appmap.set("av1_input", {"button": "AV", "friendlyName": "AV Input", "remoteCommand": '{"command": "input_av1", "num_repeats": 1, "hold_secs": 0}'}); } } function handleTunerInputs(config, tunerInput) { if (config.device_family == "roku" && (tunerInput == true || tunerInput == 'true')) { appmap.set("tuner_input", {"button": "TV Tuner", "friendlyName": "TV Tuner", "remoteCommand": '{"command": "input_tuner", "num_repeats": 1, "hold_secs": 0}'}); //appmap.set("roku-tv-channel", {"button": "Channel", "friendlyName": "Function: Input TV Channel"}); return; } if (config.device_family == "amazon-fire" && (tunerInput == true || tunerInput == 'true')) { appmap.set("tuner_input", {"button": '', "friendlyName": "Antenna", "androidName": "com.amazon.tv.livetv", "adbLaunchCommand": "adb shell input keyevent KEYCODE_TV"} ); } } function refreshAppMap(config, inputs = 0, avInputs = 0, tuner = true){ resetAppMap(); handlehdmi(config, inputs); handlecustomlaunchers(config); handleAVInputs(config, avInputs); handleTunerInputs(config, tuner); } function calculateLayoutCellHeight(h, s) { let numOfGapPixels = (( h / 56 ) * 8)-24; let standardHeight = ( h - numOfGapPixels ) / 56; let scale = s / 100; let raw = standardHeight * scale; let calculated = Math.trunc(raw * Math.pow(10, 2)) / Math.pow(10, 2); //console.log('scale is '+s+' so Im returning a cell height of '+calculated); return calculated; } function calculateLayoutCellWidth(w, s) { var medianCellWidth = 104; // between 80px and 120px depending on the screen size - adjust as needed let numOfGapPixels = (( w / medianCellWidth ) * 8)-24; let scale = s / 100; let raw = ( (w - numOfGapPixels) / medianCellWidth) * scale; let calculated = Math.ceil(raw); //console.log('scale is '+s+' so Im returning a cell width of '+calculated+' raw was '+raw); return calculated; } function calculateMasonryViewHeight(h, s) { let scale = s / 100; let raw = (h / 50) * scale; let calculated = Math.ceil(raw); //console.log('masonryViewHeight - scale is '+s+' so Im returning a cell width of '+calculated+' raw was '+raw); return calculated; } class FiremoteCard extends LitElement { // Create and return an editor element static getConfigElement() { return document.createElement("firemote-card-editor"); } static get properties() { return { hass: {}, _config: {}, }; } // Sets a default card size (height) for masonry dashboard type getCardSize(){ // https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card#sizing-in-masonry-view // TODO: This does not account for added app launcher rows var scale = this._config.scale || 100; switch (deviceAttributeQuery('defaultRemoteStyle', this._config)) { case "AF1": return calculateMasonryViewHeight(588.38, scale); break; case "AF2": return calculateMasonryViewHeight(634.08, scale); break; case "AF3": return calculateMasonryViewHeight(657.14, scale); break; case "AF4": return calculateMasonryViewHeight(715.94, scale); break; case "AF5": return calculateMasonryViewHeight(772.94, scale); break; case "AF6": return calculateMasonryViewHeight(750.45, scale); break; case "AFJTV": case "AFXF2": return calculateMasonryViewHeight(873.02, scale); break; case "AR1": return calculateMasonryViewHeight(747.5, scale); break; case "AR2": return calculateMasonryViewHeight(597, scale); break; case "AR3": return calculateMasonryViewHeight(737, scale); break; case "CC1": case "CC2": case "CC3": return calculateMasonryViewHeight(696.66, scale); break; case "NS1": case "NS2": return calculateMasonryViewHeight(681.98, scale); break; case "ON1": case "ON2": return calculateMasonryViewHeight(726.39, scale); break; case "RVRP": case "RVR": case "RSR": return calculateMasonryViewHeight(680.56, scale); break; case "RHR": case "RWR": return calculateMasonryViewHeight(652.56, scale); break; case "RTR": return calculateMasonryViewHeight(680.38, scale); break; case "XM1": case "XM2": return calculateMasonryViewHeight(793.98, scale); break; default: return; } } // Announces a default grid size for new experimental sections dashboard type getLayoutOptions() { // https://developers.home-assistant.io/docs/frontend/custom-ui/custom-card#sizing-in-sections-view // TODO: This does not account for added app launcher rows var scale = this._config.scale || 100; switch (deviceAttributeQuery('defaultRemoteStyle', this._config)) { case "AF1": return { grid_rows: calculateLayoutCellHeight(588.38, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AF2": return { grid_rows: calculateLayoutCellHeight(634.08, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AF3": return { grid_rows: calculateLayoutCellHeight(657.14, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AF4": return { grid_rows: calculateLayoutCellHeight(715.94, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AF5": return { grid_rows: calculateLayoutCellHeight(772.94, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AF6": return { grid_rows: calculateLayoutCellHeight(750.45, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AFJTV": case "AFXF2": return { grid_rows: calculateLayoutCellHeight(873.02, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "AR1": return { grid_rows: calculateLayoutCellHeight(747.5, scale), grid_columns: calculateLayoutCellWidth(184, scale), } break; case "AR2": return { grid_rows: calculateLayoutCellHeight(597, scale), grid_columns: calculateLayoutCellWidth(184, scale), } break; case "AR3": return { grid_rows: calculateLayoutCellHeight(737, scale), grid_columns: calculateLayoutCellWidth(184, scale), } break; case "CC1": case "CC2": case "CC3": return { grid_rows: calculateLayoutCellHeight(696.66, scale), grid_columns: calculateLayoutCellWidth(203.36, scale), } break; case "HO1": case "HO2": return { grid_rows: calculateLayoutCellHeight(726.39, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "HO3": case "HO4": return { grid_rows: calculateLayoutCellHeight(902.83, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; case "NS1": case "NS2": return { grid_rows: calculateLayoutCellHeight(681.98, scale), grid_columns: calculateLayoutCellWidth(137.97, scale), } break; case "ON1": case "ON2": return { grid_rows: calculateLayoutCellHeight(726.39, scale), grid_columns: calculateLayoutCellWidth(137.97, scale), } break; case "RVRP": case "RVR": case "RSR": return { grid_rows: calculateLayoutCellHeight(680.56, scale), grid_columns: calculateLayoutCellWidth(183.8, scale), } break; case "RHR": case "RWR": return { grid_rows: calculateLayoutCellHeight(652.56, scale), grid_columns: calculateLayoutCellWidth(183.8, scale), } break; case "RTR": return { grid_rows: calculateLayoutCellHeight(680.38, scale), grid_columns: calculateLayoutCellWidth(202.02, scale), } break; case "XM1": case "XM2": return { grid_rows: calculateLayoutCellHeight(793.98, scale), grid_columns: calculateLayoutCellWidth(193.97, scale), } break; default: return { //grid_rows: 12.25, //grid_columns: 2, }; } } // Returns a minimal configuration that will result in a working card static getStubConfig(e) { var androidTVEntities = Object.keys(e.entities).filter( (eid) => e.entities[eid].platform === 'androidtv' ); var appleTVEntities = Object.keys(e.entities).filter( (eid) => e.entities[eid].platform === 'apple_tv' ); var rokuEntities = Object.keys(e.entities).filter( (eid) => e.entities[eid].platform === 'roku' ); if (androidTVEntities.length > 0) { return { 'entity': androidTVEntities[0], 'device_family': 'amazon-fire', 'device_type': 'fire_tv_4_series', 'compatibility_mode': 'default', }; } else if (appleTVEntities.length > 0) { return { 'entity': appleTVEntities[0], 'device_family': 'apple-tv', 'device_type': 'appletv-4k-gen2', 'compatibility_mode': 'default', }; } else if (rokuEntities.length > 0) { return { 'entity': rokuEntities[0], 'device_family': 'roku', 'device_type': 'roku-ultra-2020', 'compatibility_mode': 'default', }; } else { return { 'entity': '', 'device_family': 'amazon-fire', 'device_type': 'fire_tv_4_series', 'compatibility_mode': 'default', }; } } setConfig(config) { if (!config.entity) { throw new Error("entity must be defined. You need to define an Apple TV, Chromecast, Fire TV, NVIDIA Shield, onn., Roku, Xiaomi Mi, or any other media_player entity"); } this._config = config; } static styles = css` ha-card { background: rgba(30,30,30,0); display: grid; justify-content: center; padding: 0; margin: auto; box-shadow: none; border: 0; outline: 0; isolation: isolate; direction: ltr; -webkit-tap-highlight-color: rgb(0 0 0 / 0%); } .hidden { display: none !important; } .mdiSubstituteIconWrapper { pointer-events: none; transform: scale(var(--sz)); } .mdiSubstitueIcon { display: var(--ha-icon-display,inline-flex); align-items: center; justify-content: center; position: relative; vertical-align: middle; fill: var(--icon-primary-color,currentcolor); width: var(--mdc-icon-size,24px); height: var(--mdc-icon-size,24px); } .shield-remote-body { background: linear-gradient(90deg, rgba(22,21,21,1) 0%, rgba(37,37,37,1) 10%, rgba(37,37,37,1) 90%, rgba(22,21,21,1) 100%); border: solid #252525 calc(var(--sz) * 0.14rem); padding: calc(var(--sz) * 1.428rem) calc(var(--sz) * 0.714rem) calc(var(--sz) * 2.143rem) calc(var(--sz) * 0.714rem); display: grid; justify-items: center; align-content: flex-start; grid-column-gap: calc(var(--sz) * 1.2rem); grid-row-gap: calc(var(--sz) * 0.5rem); grid-template-columns: 1fr 1fr; width: calc(var(--sz) * 8.286rem); min-height: calc(var(--sz) * 45rem); } .shield-remote-body.ns1-body { background: linear-gradient(90deg, rgb(28 28 28) 0%, rgb(37, 37, 37) 8%, rgb(40 40 40) 50%, rgb(37, 37, 37) 92%, rgb(28, 28, 28) 100%); border: solid #1c1c1c calc(var(--sz) * 0.14rem); border-radius: calc(var(--sz) * 1.2rem); } .roku-remote-body { display: flex; flex-direction: column; align-content: center; justify-content: flex-start; align-items: center; gap: calc(var(--sz) * 1rem); background: linear-gradient(90deg, rgba(42,42,42,1) 0%, rgba(58,58,58,1) 50%, rgba(42,42,42,1) 100%); border-radius: calc(var(--sz) * 3.25rem); border: solid #252525 calc(var(--sz) * 0.6rem); border-top: solid #292929 calc(var(--sz) * 0.6rem); width: calc(var(--sz) * 11.287rem); padding: calc(var(--sz) * 0.35rem); padding-bottom: calc(var(--sz) * 1.12rem); min-height: calc(var(--sz) * 44rem); } .roku-remote-body:has(div.rokuTag) { margin-bottom: calc(var(--sz) * 2rem); } .roku-remote-body ha-icon { transform: scale(calc(var(--sz) * .8)); } .roku-remote-body > div { display: flex; flex-wrap: nowrap; flex-direction: row; align-content: center; justify-content: center; align-items: center; gap: calc(var(--sz) * 0.35rem); } .roku-remote-body > div:not(.rokuDPadContainer) > button:active { box-shadow: none !important; transform: scale(0.975); transform-origin: center bottom; } .roku-remote-body > div:not(.rokuDPadContainer) > button:active > ha-icon { transform: scale(0.8); } .roku-remote-body.RVR > div.powerRow { display: grid; grid-template-columns: 1fr 1fr 1fr; align-items: center; justify-items: center; margin-bottom: calc(var(--sz) * 1rem); } .roku-remote-body.RTR { border-radius: calc(var(--sz) * 6rem); padding: calc(var(--sz) * 1rem); } .roku-remote-body.RWR > div.powerRow, .roku-remote-body.RTR > div.powerRow, .roku-remote-body.RHR > div.powerRow { margin-bottom: calc(var(--sz) * 1.25rem); } .roku-remote-body > div.micRow { height: calc(var(--sz) * 0.4rem); aspect-ratio: 1; background: black; border-radius: 100%; margin-top: calc(var(--sz) * 0.4rem); } .roku-remote-body.RVR div.micRow { height: calc(var(--sz) * 0.4rem); aspect-ratio: 1; background: black; border-radius: 100%; } .roku-remote-body.RSR > div.backHomeRow { padding-top: calc(var(--sz) * 5rem); } .roku-remote-body > div.backHomeRow > button{ height: calc(var(--sz) * 2.5rem); width: calc(var(--sz) * 5.25rem); border: solid #090909 calc(var(--sz) * 0.125rem); border-radius: calc(var(--sz) * 0.7rem); box-shadow: 0px 5px 2px 0px #0000001a; } .roku-remote-body > div.rokuDPadContainer { background: linear-gradient(175deg, rgb(42 42 42) 0%, rgb(51 51 51) 100%); padding: calc(var(--sz) * 0.3rem); border-radius: calc(var(--sz) * 1.5rem); display: grid; grid-template-columns: 31% 38% 31%; grid-template-rows: 31% 38% 31%; grid-gap: 0; width: calc(var(--sz) * 10.15rem); height: calc(var(--sz) * 10.15rem); overflow: hidden; } .roku-remote-body.RTR > div.rokuDPadContainer { padding: calc(var(--sz) * 0.75rem); border-radius: 100%; } .rokuDPadContainer > button { background: #662d91; color: rgb(198, 198, 198); height: 100%; width: 100%; margin: 0; cursor: pointer; line-height: normal; user-select: none; display: grid; place-content: center; border: 0; aspect ratio: 1; font-size: calc(var(--sz) * 1.275rem); position: relative; z-index: 2; } .rokuDPadContainer > button > ha-icon { transform: scale(calc(var(--sz) * 1.3)); } .rokuDPadContainer > button:active > ha-icon, .rokuDPadContainer > button:active > .okCircle { color: #bcbcbc; transform: scale(calc(var(--sz) * 0.85)); } .roku-remote-body #up-button { transform-origin: bottom center; border-radius: calc(var(--sz) * 1rem) calc(var(--sz) * 1rem) 0 0; position: relative; } .roku-remote-body #left-button { transform-origin: center right; border-radius: calc(var(--sz) * 1rem) 0 0 calc(var(--sz) * 1rem); box-shadow: 0px 5px 2px 0px #0000001a; } .roku-remote-body #right-button { border-radius: 0 calc(var(--sz) * 1rem) calc(var(--sz) * 1rem) 0; transform-origin: center left; box-shadow: 0px 5px 2px 0px #0000001a; z-index: 1; } .roku-remote-body #down-button { transform-origin: top center; border-radius: 0 0 calc(var(--sz) * 1rem) calc(var(--sz) * 1rem); box-shadow: 0px 5px 2px 0px #0000001a; } .roku-remote-body #center-button { z-index: 3; } .roku-remote-body #up-button::before { content: ''; position: absolute; margin: 0; padding: 0; bottom: 0; left: calc(var(--sz) * -1.95rem); border-radius: 100%; height: calc(var(--sz) * 2rem); width: calc(var(--sz) * 2rem); background: transparent; box-shadow: calc(var(--sz) * 1rem) calc(var(--sz) * 1rem) 0 rgb(102, 45, 145); } .roku-remote-body #up-button::after { content: ''; position: absolute; margin: 0; padding: 0; bottom: 0; right: calc(var(--sz) * -1.95rem); border-radius: 100%; height: calc(var(--sz) * 2rem); width: calc(var(--sz) * 2rem); background: transparent; box-shadow: calc(var(--sz) * -1rem) calc(var(--sz) * 1rem) 0 rgb(102, 45, 145); } .roku-remote-body #down-button::before { content: ''; position: absolute; margin: 0; padding: 0; top: 0; left: calc(var(--sz) * -1.95rem); border-radius: 100%; height: calc(var(--sz) * 2rem); width: calc(var(--sz) * 2rem); background: transparent; box-shadow: calc(var(--sz) * 1rem) calc(var(--sz) * -1rem) 0 rgb(102, 45, 145); } .roku-remote-body #down-button::after { content: ''; position: absolute; margin: 0; padding: 0; top: 0; right: calc(var(--sz) * -1.95rem); border-radius: 100%; height: calc(var(--sz) * 2rem); width: calc(var(--sz) * 2rem); background: transparent; box-shadow: calc(var(--sz) * -1rem) calc(var(--sz) * -1rem) 0 rgb(102, 45, 145); } .rokuDPadContainer > button:not(#center-button):active { box-shadow: unset; transform: scale(0.98); } .okCircle { padding: calc(var(--sz) * .75rem); border-radius: 100%; border: calc(var(--sz) * 0.15rem) solid rgb(88 38 125); background: linear-gradient(175deg, rgb(165 165 165 / 20%) 10%, rgb(29 29 29 / 20%) 100%); margin: 0; place-self: center; display: flex; place-content: center; align-items: center; user-select: none; pointer-events: none; aspect-ratio: 1/1; overflow: hidden; } .rokuDPadContainer > button:active > .okCircle { color: #bcbcbc; transform: scale(calc(var(--sz) * 0.8)); filter: brightness(0.9); } .roku-remote-body > div.replayVoiceOptionsRow, .roku-remote-body > div.scrubRow, .roku-remote-body > div.personalShortcutsRow { width: calc(var(--sz) * 10.15rem); display: grid; grid-template-columns: 35% 22% 35%;; justify-content: center; place-content: center space-between; place-items: center; justify-items: stretch; align-items: center; } .roku-remote-body.RVR > div.replayVoiceOptionsRow { grid-template-columns: 25% 38% 25%; } .roku-remote-body.RSR > div.replayVoiceOptionsRow, .roku-remote-body.RTR > div.replayVoiceOptionsRow { grid-template-columns: 47% 47%; } .roku-remote-body > div.replayVoiceOptionsRow button, .roku-remote-body > div.scrubRow button, .roku-remote-body > div.personalShortcutsRow button { height: calc(var(--sz) * 2.25rem); width: 100%; border: solid #090909 calc(var(--sz) * 0.125rem); border-radius: calc(var(--sz) * 0.7rem); box-shadow: 0px 5px 2px 0px #0000001a; } .roku-remote-body > div.scrubRow { grid-template-columns: 22% 48% 22%; } .roku-remote-body.RVR > div.scrubRow, .roku-remote-body.RSR > div.scrubRow, .roku-remote-body.RWR > div.scrubRow { margin-bottom: calc(var(--sz) * 0.75rem); } .roku-remote-body > div.scrubRow button { height: calc(var(--sz) * 3rem); } .roku-remote-body > div.personalShortcutsRow { grid-template-columns: 31% 31%; gap: calc(var(--sz) * 1.75rem); justify-content: center; padding: calc(var(--sz) * 0.3rem) 0 calc(var(--sz) * 0.6rem) 0; } .roku-remote-body > div.personalShortcutsRow > button > ha-icon { transform: scale(var(--sz)); } .roku-remote-body .appLauncherAppsContainer { display: grid; grid-template-columns: 50% 50%; grid-row-gap: calc(var(--sz) * 1rem); align-items: center; justify-items: center; justify-content: center; align-content: center; } .roku-remote-body.RHR .appLauncherAppsContainer { padding: calc(var(--sz) * 1rem) 0 calc(var(--sz) * 4rem) 0; } .roku-remote-body.RWR .appLauncherAppsContainer { padding-bottom: calc(var(--sz) * 6rem); } .roku-remote-body .appLauncherAppsContainer > button { height: calc(var(--sz) * 1.8rem); width: calc(var(--sz) * 5.1426rem); border-radius: calc(var(--sz) * 0.7rem); box-sizing: border-box; align-items: center; } .roku-remote-body .appLauncherVerticalAppsContainer { display: grid; margin-top: calc(var(--sz) * 1.25rem); gap: calc(var(--sz) * 1rem); } .rokuBrandLogo { padding: calc(var(--sz) * 1rem) 0; } .RHR .rokuBrandLogo, .RWR .rokuBrandLogo { position: absolute; bottom: calc(var(--sz) * 2rem); } .RTR .rokuBrandLogo { padding: calc(var(--sz) * 1rem) 0 calc(var(--sz) * 2.25rem) 0; } .rokuBrandLogo svg { height: calc(var(--sz) * 1.15rem); max-width: calc(var(--sz) * 11.287rem); } .RHR .rokuBrandLogo svg { height: calc(var(--sz) * 0.8rem); } .RWR .rokuBrandLogo svg { height: calc(var(--sz) * 3.1rem); } .roku-remote-body .rokuTag { position: absolute; height: calc(var(--sz) * 2.2rem); width: calc(var(--sz) * 5.6rem); background: repeating-linear-gradient(#662D91, #662D91 calc(var(--sz) * 0.15rem), #3b1955 calc(var(--sz) * 0.15rem), #3b1955 calc(var(--sz) * 0.3rem)); bottom: 0; overflow: hidden; box-sizing: border-box; padding: calc(var(--sz) * 1.1rem); border: solid #662d91 calc(var(--sz) * 0.1rem); border-top: none; border-bottom: none; -webkit-clip-path: polygon(7% 5%, 93% 5%, 100% 0, 100% 100%, 0 100%, 0 0); clip-path: polygon(7% 5%, 93% 5%, 100% 0, 100% 100%, 0 100%, 0 0); } .chromecast-remote-body { background: #ebebea; border-radius: calc(var(--sz) * 6rem); border: solid #cfcfcf calc(var(--sz) * 0.1rem); width: calc(var(--sz) * 11.287rem); padding: calc(var(--sz) * 0.35rem); padding-bottom: calc(var(--sz) * 4rem); display: grid; justify-items: center; align-content: flex-start; grid-column-gap: calc(var(--sz) * 1.2rem); grid-row-gap: calc(var(--sz) * 0.6rem); grid-template-columns: 1fr 1fr; min-height: calc(var(--sz) * 37rem); } .chromecast-remote-body.CC2 { background: #cad4d8; } .chromecast-remote-body.CC3 { background: #e1d2cc; } .apple-remote-body { background: #b5b5b5; background: linear-gradient(0deg, rgba(147,148,150,1) 0%, rgba(207,211,213,1) 100%); border: solid #d1d1d1 calc(var(--sz) * 0.05rem); border-radius: calc(var(--sz) * 1.75rem); display: grid; justify-items: center; align-content: flex-start; grid-column-gap: calc(var(--sz) * 1rem); grid-row-gap: calc(var(--sz) * 0.5rem); grid-template-columns: 1fr 1fr; width: calc(var(--sz) * 13rem); min-height: calc(var(--sz) * 51rem); padding-bottom: calc(var(--sz) * 1.5rem); } .apple-remote-body.AR1 { padding-top: calc(var(--sz) * 4.75rem); min-height: calc(var(--sz) * 47rem); grid-column-gap: calc(var(--sz) * 0.25rem); grid-row-gap: calc(var(--sz) * 0.75rem); font-size: calc(var(--sz) * 1rem); } .apple-remote-body.AR2 { background: linear-gradient(30deg, rgb(0, 0, 0) 0%, rgb(41, 41, 41) 70%); display: grid; grid-template-columns: 1fr; grid-template-rows: auto 1fr; grid-row-gap: 0; border: solid #2d2d2d calc(var(--sz) * 0.01rem); min-height: calc(var(--sz) * 41rem); overflow: hidden; } .AR2TopSection, .AR2BottomSection { display: grid; justify-items: center; align-content: flex-start; grid-column-gap: calc(var(--sz) * 1.5rem); grid-row-gap: calc(var(--sz) * 0.5rem); grid-template-columns: 1fr 1fr; box-sizing: border-box; } .AR2TopSection { background: linear-gradient(0deg, rgba(46,46,46,1) 0%, rgba(59,59,59,1) 100%); height: 100%; width: 100%; padding-bottom: calc(var(--sz) * 0.25rem); } .AR2BottomSection { padding-top: calc(var(--sz) * 0.5rem); } .apple-remote-body.AR2 > div > button:nth-of-type(odd), .apple-remote-body.AR3 > button:nth-of-type(odd) { justify-self: self-end; transform-origin: center center; } .apple-remote-body.AR2 > div > button:nth-of-type(even), .apple-remote-body.AR3 > button:nth-of-type(even) { justify-self: self-start; transform-origin: center center; } .remote-body { background: linear-gradient(90deg, rgba(27,27,27,1) 0%, rgba(37,37,37,1) 8%, rgba(55,55,55,1) 50%, rgba(37,37,37,1) 92%, rgba(27,27,27,1) 100%); border: solid #252525 calc(var(--sz) * 0.14rem); border-radius: calc(var(--sz) * 8rem) calc(var(--sz) * 8rem) calc(var(--sz) * 8rem) calc(var(--sz) * 8rem) / calc(var(--sz) * 2.5rem) calc(var(--sz) * 2.5rem) calc(var(--sz) * 2.5rem) calc(var(--sz) * 2.5rem); padding: calc(var(--sz) * 1.428rem) calc(var(--sz) * 0.714rem) calc(var(--sz) * 2.143rem) calc(var(--sz) * 0.714rem); display: grid; justify-items: center; grid-column-gap: calc(var(--sz) * 0.14rem); grid-row-gap: calc(var(--sz) * 0.5rem); grid-template-columns: 1fr 1fr 1fr; width: calc(var(--sz) * 12.286rem); } .remote-body.AF6 { border-radius: calc(var(--sz) * 2.8rem); } .XM2 { min-height: calc(var(--sz) * 53rem); grid-template-rows: repeat(7, auto) 1fr; border-radius: calc(var(--sz) * 2rem); } .AL1, .AL2 { display: block; width: unset; border-radius: calc(var(--sz) * 0.5rem); padding: calc(var(--sz) * 1.4rem) calc(var(--sz) * 0.714rem); } .two-col-span { grid-column-start: 1; grid-column-end: 3; width: 100%; display: grid; justify-content: center; grid-row-gap: calc(var(--sz) * 0.143rem); align-content: center; } .three-col-span { grid-column-start: 1; grid-column-end: 4; width: 100%; display: grid; grid-column-gap: calc(var(--sz) * 0.143rem); grid-template-columns: 50% 50%; align-content: center; } .apple-tv-top { display: grid; grid-template-columns: 1fr 1fr 1fr; width: calc(var(--sz) * 11.286rem); justify-items: center; align-items: center; justify-content: center; align-content: center; padding-top: calc(var(--sz) * 0.85rem); } .remote-body #channel-up-button, .remote-body #channel-down-button { --mdc-icon-size: 38px; } .nsappsgrid { display: grid; grid-row-gap: calc(var(--sz) * 0.6rem); } .afappsgrid { display: grid; margin-top: calc(var(--sz) * 0.57rem); row-gap: calc(var(--sz) * 0.9rem); justify-items: center; } .XM2 .afappsgrid { margin-bottom: calc(var(--sz) * 0.57rem); } .appLauncherAppsContainer { display: flex; flex-wrap: wrap; flex-direction: row; align-content: center; align-items: center; justify-content: center; gap: calc(var(--sz) * 0.9rem); } .ns1-wings { grid-column-start: 1; grid-column-end: 3; width: 100%; height: calc(var(--sz) * 25rem); margin-top: calc(var(--sz) * -1rem); display: grid; grid-template-columns: 1fr 27% 1fr; } #ns1spine { display: grid; padding: calc(var(--sz) * 4rem) 0; } #ns1spine button { all: unset; background: transparent; } #ns1spine button:active { background: #363636; } #wingL { background: rgb(28 28 28); -webkit-clip-path: polygon(100% 15%, 100% 85%, 0 100%, 0 0); clip-path: polygon(100% 15%, 100% 85%, 0 100%, 0 0); margin-left: calc(var(--sz) *-.714rem); border-right: solid #121212 calc(var(--sz) * .15rem); } #wingR { background: rgb(28 28 28); -webkit-clip-path: polygon(100% 0, 100% 100%, 0 85%, 0 15%); clip-path: polygon(100% 0, 100% 100%, 0 85%, 0 15%); margin-right: calc(var(--sz) *-.714rem); border-left: solid #121212 calc(var(--sz) * .15rem); } .ALControlsContainer { display: flex; gap: calc(var(--sz) * .25rem); place-items: center; padding-top: calc(var(--sz) * 2rem); margin-top: calc(var(--sz) * 2rem); border-top: groove rgb(0 0 0 / 42%) calc(var(--sz) * 0.2rem); justify-content: space-evenly; flex-wrap: wrap; flex-direction: row; width: 100%; } .ALControlsContainer.noApps { padding-top: 0; margin-top: 0; border-top: 0; } .ALControlsContainer > div { display: grid; justify-items: center; justify-content: center; } .left-pocket-controls { display: flex; column-gap: calc(var(--sz) * 2rem); row-gap: calc(var(--sz) * 0.5rem); flex-wrap: wrap; justify-content: center; } .ALControlsContainer .row { display: flex; align-items: center; gap: calc(var(--sz) * 0.5rem); padding: calc(var(--sz) * 0.75rem); background: rgb(255 255 255 / 6%); border: inset #000 calc(var(--sz) * 0.2rem); } .ALControlsContainer .row.noframes { background: none; border: 0; } .right-pocket-controls > .row { column-gap: calc(var(--sz) * 0.5rem); line-height: calc(var(--sz) * .4rem); } .remote-button { height: calc(var(--sz) * 3.572rem); width: calc(var(--sz) * 3.572rem); border: solid black calc(var(--sz) * 0.0714rem); border-radius: 100%; display: grid; justify-content: center; align-content: center; color: rgb(198 198 198); background: rgb(33 33 33); box-shadow: rgb(0 0 0 / 13%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.143rem 0); cursor: pointer; line-height: normal; user-select: none; padding: calc(var(--sz) * 0.2857rem); } #power-button { height: calc(var(--sz) * 2.8rem); width: calc(var(--sz) * 2.8rem); margin-bottom: calc(var(--sz) * -0.643rem); } .shield-remote-body .remote-button { height: calc(var(--sz) * 3rem); width: calc(var(--sz) * 3rem); } .apple-remote-body .remote-button { height: calc(var(--sz) * 4.75rem); width: calc(var(--sz) * 4.75rem); } .apple-remote-body.AR2 .remote-button { height: calc(var(--sz) * 4.5rem); width: calc(var(--sz) * 4.5rem); } .apple-remote-body.AR1 #back-button.remote-button, .apple-remote-body.AR2 #back-button.remote-button { font-size: calc(var(--sz) * 1rem); font-weight: 600; } .apple-remote-body.AR2 #search-button.remote-button, .apple-remote-body.AR3 #back-button.remote-button { background: linear-gradient(rgb(0, 0, 0) 0%, rgb(48, 48, 48) 100%); outline: solid #2b2b2b calc(var(--sz) * 0.01rem); } .chromecast-remote-body .remote-button, .chromecast-remote-body #keyboard-button { background: #fff; border: solid #bfbfbf calc(var(--sz) * 0.02em); height: calc(var(--sz) * 4.2rem); width: calc(var(--sz) * 4.2rem); box-shadow: rgb(0 0 0 / 5%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.1428rem) 0; } .chromecast-remote-body.CC2 .remote-button { background: #d5e0e4; border-color: #a6a6a6; } .chromecast-remote-body.CC3 .remote-button { background: #efddd6; border-color: #bababa; } .chromecast-remote-body .srcButton { border: solid #bfbfbf calc(var(--sz) * 0.02em); height: calc(var(--sz) * 4.2rem); width: calc(var(--sz) * 4.2rem); border-radius: 100%; } .chromecast-remote-body #keyboard-button { background: #484848; } .chromecast-remote-body #keyboard-button > ha-icon { color: rgb(235, 235, 234); } .chromecast-remote-body.CC2 #keyboard-button { background: linear-gradient(0deg, rgba(186,214,198,1) 0%, rgba(222,239,231,1) 90%); border-color: #a6a6a6; } .chromecast-remote-body.CC3 #keyboard-button { background: linear-gradient(0deg, rgba(221,158,152,1) 0%, rgba(228,186,180,1) 90%); border-color: #a6a6a6; } .chromecast-remote-body.CC2 #keyboard-button > ha-icon, .chromecast-remote-body.CC3 #keyboard-button > ha-icon { color: #fff; } .apple-remote-body .srcButton { border: solid #bfbfbf calc(var(--sz) * 0.02em); height: calc(var(--sz) * 4.75rem); width: calc(var(--sz) * 4.75rem); border-radius: 100%; } .shield-remote-body #power-button, .XM2 #power-button{ height: calc(var(--sz) * 3rem); width: calc(var(--sz) * 3rem); margin-bottom: unset; } .XM2 #power-button, .XM2 #keyboard-button, .AL2 #power-button, .AL2 #keyboard-button { height: calc(var(--sz) * 3.572rem); width: calc(var(--sz) * 3.572rem); } .roku-remote-body #keyboard-button { height: calc(var(--sz) * 2.25rem); width: 100%; } .AL2 #power-button { margin-bottom: unset; } .right { display: flex; width: 100%; justify-content: flex-end; } .apple-remote-body #power-button { background: none; border: gray 0.01rem solid; transform: scale(0.75); margin: 0; } .shield-remote-body #power-button > ha-icon { color: #851313; } .apple-remote-body #power-button > ha-icon { color: rgb(33, 33, 33); } .roku-remote-body #power-button > ha-icon { color: #970000; } .XM2 #power-button > ha-icon { color: #835437; } #headset-button { height: calc(var(--sz) * 2.8rem); width: calc(var(--sz) * 2.8rem); margin-bottom: calc(var(--sz) * -0.643rem); } .shield-remote-body #home-button { --mdc-icon-size: 17px; } .shield-remote-body.ns1-body #home-button { --mdc-icon-size: 23px; } .apple-remote-body #back-button { --mdc-icon-size: 36px; } .shield-remote-body #back-button { --mdc-icon-size: 41px; } .notch { background: #181818; height: calc(var(--sz) * 1rem); width: calc(var(--sz) * 0.4rem); margin-top: calc(var(--sz) * -0.5rem); border-radius: calc(var(--sz) * 0.2rem); } .apple-remote-body .notch { margin: 0; width: calc(var(--sz) * 1rem); height: calc(var(--sz) * 0.4rem); } .chromecast-remote-body .notch { height: calc(var(--sz) * 0.8rem); width: calc(var(--sz) * 0.3rem); rotate: 90deg; margin: 0; } .shieldNotch { grid-column: 1 / 3; background: rgb(24, 24, 24); height: calc(var(--sz) * 0.3rem); width: calc(var(--sz) * 0.75rem); margin-top: calc(var(--sz) * -0.5rem); border-radius: calc(var(--sz) * 0.2rem); } .notchtall { margin-bottom: calc(var(--sz) * 1.65rem); } .ns1-body .shieldNotch { height: calc(var(--sz) * 0.75rem); width: calc(var(--sz) * 2.2rem); border: solid #282828 calc(var(--sz) * 0.12rem); border-radius: calc(var(--sz) * 0.5rem); } #keyboard-button { height: calc(var(--sz) * 3rem); width: calc(var(--sz) * 3rem); } .teal { background: #09727e; } .dpadContainer{ grid-column: 1 / 4; display: grid; margin-bottom: calc(var(--sz) * 0.65rem); width: calc(var(--sz) * 11.286rem); height: calc(var(--sz) * 11.286rem); position: relative; } .chromecast-remote-body .dpadContainer, .apple-remote-body .dpadContainer { grid-column: 1 / 3; margin-bottom: 0; } .apple-remote-body.AR2 .dpadContainer { overflow: hidden; } .XM2 .dpadContainer, .XM.dpadContainer { align-items: center; justify-items: center; position: relative; } .shieldDpad { grid-column: 1 / 3; width: calc(var(--sz) * 8.2rem); height: calc(var(--sz) * 8.2rem); position: relative; } .ALControlsContainer .dpadContainer { grid-column: unset; } .centerbutton{ all: unset; cursor: pointer; border: solid black calc(var(--sz) * 0.0714rem); margin-left: calc(var(--sz) * 2.357rem); margin-top: calc(var(--sz) * 2.357rem); width: calc(var(--sz) * 6.428rem); height: calc(var(--sz) * 6.428rem); border-radius: 100%; position: absolute; background: rgba(55,55,55,1); box-shadow: inset 0 0.calc(var(--sz) * 2857rem) calc(var(--sz) * 0.1428rem) calc(var(--sz) * -0.1428rem) #000000d9; z-index: 5; } .directionButtonContainer{ transform: rotate(45deg); border: calc(var(--sz) * 0.0714rem) solid black; display: grid; grid-template-columns: auto auto; border-radius: 100%; overflow: hidden; box-shadow: rgb(20 20 20) calc(var(--sz) * 0.1428rem) calc(var(--sz) * 0.1428rem) calc(var(--sz) * 0.4285rem); position: relative; } .AR2 .directionButtonContainer { margin-top: calc(var(--sz) * -1.4rem); margin-left: calc(var(--sz) * -1.35rem); border-radius: 0; border: 0; overflow: unset; box-shadow: none; } .chromecast-remote-body .directionButtonContainer, .CC .directionButtonContainer { border: calc(var(--sz) * 0.0714rem) solid #9d9d9d; box-shadow: rgb(0 0 0 / 29%) 0 0 calc(var(--sz) * 0.4rem) calc(var(--sz) * 0.02rem); } .dpadbutton{ all: unset; cursor: pointer; width: calc(var(--sz) * 5.5714rem); height: calc(var(--sz) * 5.5714rem); background: #141414; color: white; font-size: calc(var(--sz) * 1rem); outline: solid #2e2e2e calc(var(--sz) * 0.0714rem); } .AR2 .dpadbutton { width: calc(var(--sz) * 7rem); height: calc(var(--sz) * 7rem); outline: none; } .XM2 .dpadbutton, .XM .dpadbutton { background: rgb(28 28 28); border: solid rgb(15 15 15) calc(var(--sz) * 0.03rem); outline: none; box-sizing: border-box; } .XM2 .dpadbutton:active, .XM .dpadbutton:active { background: rgb(24 24 24); border: solid #000 calc(var(--sz) * 0.15rem); } .chromecast-remote-body.CC1 .dpadbutton, .CC .dpadbutton { background: #fff; outline: solid #c5c5c5 calc(var(--sz) * 0.0714rem); } .chromecast-remote-body.CC2 .dpadbutton, .CC2 .dpadbutton { background: #cad4d8; outline: solid #c5c5c5 calc(var(--sz) * 0.0714rem); } .chromecast-remote-body.CC3 .dpadbutton, .CC3 .dpadbutton { background: #e1d2cc; outline: solid #c5c5c5 calc(var(--sz) * 0.0714rem); } .AR3 .dpadbutton:nth-child(1)::after, .AR1 .dpadbutton:nth-child(1)::after { content: "•"; text-align: center; display: block; font-weight: 600; color: rgb(198, 198, 198); padding-right: calc(var(--sz) * 1.2rem); padding-bottom: calc(var(--sz) * 1.2rem); } .AR3 .dpadbutton:nth-child(2)::after, .AR1 .dpadbutton:nth-child(2)::after { content: "•"; text-align: center; display: block; font-weight: 600; color: rgb(198, 198, 198); padding-left: calc(var(--sz) * 1.2rem); padding-bottom: calc(var(--sz) * 1.2rem); } .AR3 .dpadbutton:nth-child(3)::after, .AR1 .dpadbutton:nth-child(3)::after { content: "•"; text-align: center; display: block; font-weight: 600; color: rgb(198, 198, 198); padding-right: calc(var(--sz) * 1.2rem); padding-top: calc(var(--sz) * 1.2rem); } .AR3 .dpadbutton:nth-child(4)::after, .AR1 .dpadbutton:nth-child(4)::after { content: "•"; text-align: center; display: block; font-weight: 600; color: rgb(198, 198, 198); padding-left: calc(var(--sz) * 1.2rem); padding-top: calc(var(--sz) * 1.2rem); } .dpadbutton:active { background: #282828; } .chromecast-remote-body .dpadbutton:active, .CC .dpadbutton:active { background: #efefef; } .chromecast-remote-body.CC2 .dpadbutton:active { background: #bfcbd0; } .chromecast-remote-body.CC3 .dpadbutton:active { background: #dbcac3; } .dpadbuttonShield { width: calc(var(--sz) * 4.101rem); height: calc(var(--sz) * 4.101rem); } .centerbuttonShield { width: calc(var(--sz) * 5rem); height: calc(var(--sz) * 5rem); margin: 0px; padding: 0px; place-self: center; position: absolute; background: rgba(37,37,37,1); background: radial-gradient(circle, rgba(28,28,28,1) 0%, rgba(37,37,37,1) 100%); } .XM2 .centerbutton, .XM .centerbutton { background: rgb(28, 28, 28); border: solid black calc(var(--sz) * 0.2rem); width: calc(var(--sz) * 5rem); height: calc(var(--sz) * 5rem); margin: 0px; position: absolute; box-sizing: border-box; } .chromecast-remote-body .centerbutton, .CC .centerbutton { background: linear-gradient(rgb(226 226 226) 0%, rgb(255, 255, 255) 70%); box-shadow: inset rgb(0 0 0 / 10%) 0 calc(var(--sz) * 0.15rem) calc(var(--sz) * 0.4rem); border: solid #dddddd calc(var(--sz) * 0.0714rem); width: calc(var(--sz) * 4rem); height: calc(var(--sz) * 4rem); place-self: center; position: absolute; margin: 0; padding: 0; } .chromecast-remote-body.CC2 .centerbutton { background: linear-gradient(0deg, rgb(208 218 225) 5%, rgb(181 194 199) 100%); border: solid #bbc5c9 calc(var(--sz)* 0.0714rem); } .chromecast-remote-body.CC3 .centerbutton { background: linear-gradient(0deg, rgba(245,226,219,1) 0%, rgba(196,180,174,1) 100%); border: solid #d9d1d1 calc(var(--sz)* 0.0714rem); } .apple-remote-body .centerbutton, .AR3 .centerbutton { background: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgba(48,48,48,1) 100%); width: calc(var(--sz) * 6rem); height: calc(var(--sz) * 6rem); margin-left: calc(var(--sz) * 2.65rem); margin-top: calc(var(--sz) * 2.65rem); box-sizing: border-box; transform-origin: center bottom; outline: solid #2e2e2e calc(var(--sz) * 0.0714rem); } .AR1 .centerbutton { background: linear-gradient(180deg, rgb(147, 148, 150) 0%, rgb(207, 211, 213) 100%); } .apple-remote-body.AR2 .centerbutton { border-radius: 0; border: 0; outline: 0; transform: scale(.75); transform-origin: center; text-align: center; } .apple-remote-body.AR2 .dpadContainer button { background: none; color: transparent; } .apple-remote-body.AR2 .dpadContainer button:hover, .apple-remote-body.AR2 .dpadContainer button:active { color: #636363; text-align: center; } .centerbutton:active { transform: scale(95%); } .chromecast-remote-body .centerbutton:active, .CC .centerbutton:active { transform: none; box-shadow: inset rgb(0 0 0 / 19%) 0 calc(var(--sz) * 0.25rem) calc(var(--sz) * 0.4375rem) calc(var(--sz) * 0.125rem); } .apple-remote-body .centerbutton:active, .AR3 .centerbutton:active { transform: none; border: calc(var(--sz) * 0.2rem) solid black; } .AR3 .centerbutton:active { background: linear-gradient(180deg, rgba(0,0,0,1) 15%, rgba(48,48,48,1) 100%); } .XM1 .centerbutton:active, .XM2 .centerbutton:active, .XM .centerbutton:active { transform: none; border: solid #090909 calc(var(--sz) * 0.24rem); background: rgb(26 26 26); } .minimal.dpadContainer { align-items: center; justify-items: center; display: grid; grid-template-columns: auto calc(var(--sz) * 5rem) auto; grid-template-rows: auto calc(var(--sz) * 5rem) auto; margin: calc(var(--sz) * 1rem); height: calc(var(--sz) * 10.3rem); aspect-ratio: 1/1; } .minimal .directionButtonContainer { border-radius: 30%; border: none; box-shadow: none; } .minimal .dpadbutton { background: transparent; outline: none; display: grid; height: 100%; width: 100%; transform-origin: center center; align-self: start; justify-items: center; justify-content: center; align-content: center; } .minimal .dpadbutton > svg { pointer-events: none; } .minimal .dpadbutton:active { transform: scale(0.8); } .minimal .dpadbutton > svg { display: inline-block; transform: scale(calc(var(--sz) * .55)); } .minimal .centerbutton { border: calc(var(--sz) * 0.175rem) solid rgb(179 179 179); background: transparent; border-radius: 25%; width: calc(var(--sz) * 3rem); height: calc(var(--sz) * 3rem); margin: 0; position: unset; } .minimal.dpadContainer #up-button { align-content: flex-start; } .minimal.dpadContainer #right-button { justify-content: flex-end; } .minimal.dpadContainer #down-button { align-content: flex-end; } .minimal.dpadContainer #left-button { justify-content: flex-start; } .remote-button:active { box-shadow: inset rgb(0 0 0 / 13%) 0 calc(var(--sz) * 0.2857rem) calc(var(--sz) * 0.1428rem) 0; } .remote-button > ha-icon { color: #c6c6c6; } .remote-button:active > ha-icon, .remote-button:active > .mdiSubstituteIconWrapper { color: #bcbcbc; transform: scale(calc(var(--sz) * 0.85)); } .chromecast-remote-body ha-icon { color: #686868; } .chromecast-remote-body .remote-button:active { box-shadow: rgb(0 0 0 / 5%) 0px calc(var(--sz) * 0.125rem) calc(var(--sz) * 0.125rem) calc(var(--sz) * 0.1875rem) inset; } .chromecast-remote-body .remote-button:active > ha-icon { color: #000; } .square { border-radius: 0; border: 0; padding: 0; } .round-top { border-radius: 100% 100% 0 0; border-bottom: 0; box-shadow: none; height: calc(var(--sz) * 3.92857rem); margin-bottom: calc(var(--sz) * -0.5rem); } .round-bottom { border-radius: 0 0 100% 100%; border-top: 0; height: calc(var(--sz) * 3.92857rem); margin-top: calc(var(--sz) * -0.5rem); } .apple-remote-body .round-bottom, .apple-remote-body.AR2 .round-bottom { height: calc(var(--sz) * 5.25rem); } .square:active, .round-bottom:active { box-shadow: none; } .remote-body #volume-up-button, .remote-body #volume-down-button { font-size: calc(var(--sz) * 2.5rem); } .apple-remote-body #volume-up-button, .apple-remote-body #volume-down-button { font-size: calc(var(--sz) * 2.25rem); } .remote-body #programmable-one-button, .remote-body #programmable-two-button { font-size: calc(var(--sz) * 1.75rem); } .remote-body #volume-up-button:active, .remote-body #volume-down-button:active { font-size: calc(var(--sz) * 2rem); } .apple-remote-body #volume-up-button:active, .apple-remote-body #volume-down-button:active { font-size: calc(var(--sz) * 1.75rem); } .remote-body #programmable-one-button:active, .remote-body #programmable-two-button:active { font-size: calc(var(--sz) * 1.45rem); } .srcButton { height: calc(var(--sz) * 2rem); width: calc(var(--sz) * 5.714rem); border: solid #090909 calc(var(--sz) * 0.0714rem); border-radius: calc(var(--sz) * 2rem); display: grid; justify-items: center; align-content: center; color: rgb(198 198 198); background: rgb(33 33 33); box-shadow: rgb(0 0 0 / 25%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.1428rem) 0; cursor: pointer; line-height: normal; user-select: none; font-size: calc(var(--sz) * 1.14rem); padding: calc(var(--sz) * 0.285rem); white-space: nowrap; transform: translate3d(0,0,0); /* because ios safari mobile */ overflow: hidden; transition: filter 250ms; filter: var(--appButtonFilter); } .apple-remote-body .srcButton { border: none; box-shadow: rgb(0 0 0 / 13%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.143rem 0); transition: none; } .chromecast-remote-body .srcButton { filter: grayscale(40%); box-shadow: rgb(0 0 0 / 5%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.1428rem) 0; } .srcButton:active, .srcButton.appActive { filter: none; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.142rem) rgb(255 255 255 / 20%); transition: filter 0s; } .srcButton.appActiveUnknown { filter: grayscale(0%) brightness(100%); transition: filter 0s; } .shield-remote-body .srcButton { height: calc(var(--sz) * 3rem); width: calc(var(--sz) * 8rem); } .srcButton:active { filter: brightness(90%); transform: scale(.95); box-shadow: none; transform-origin: center bottom; } .chromecast-remote-body .srcButton:active { filter: none; } .srcButton svg { pointer-events: none; max-width: calc(var(--sz) * 4.5rem); max-height: calc(var(--sz) * 1.6rem); } .shield-remote-body .srcButton svg { max-height: calc(var(--sz) * 2.1rem); max-width: calc(var(--sz) * 6.8rem); } .chromecast-remote-body .srcButton svg, .apple-remote-body .srcButton svg { max-width: calc(var(--sz) * 3.25rem); max-height: calc(var(--sz) * 3.25rem); } .apple-remote-body .srcButton svg { max-width: calc(var(--sz) * 3.55rem); max-height: calc(var(--sz) * 3.55rem); } .chromecastBottomIndentedRow { display: grid; width: calc(100% - (var(--sz) * 1.2rem) - (var(--sz) * 0.4rem * 2)); padding: calc(var(--sz) * 0.4rem); box-sizing: border-box; grid-template-columns: auto 1fr auto; box-shadow: rgb(0 0 0 / 28%) 0 calc(var(--sz) * 0.12rem) calc(var(--sz) * 0.4rem) inset; border-radius: calc(var(--sz) * 5rem); grid-column: 1 / 3; place-items: center; margin-top: calc(var(--sz) * 1rem); } .chromecastBottomIndentedRow .remote-button, .chromecastBottomIndentedRow .input-button { height: calc(var(--sz) * 2.3rem) !important; width: calc(var(--sz) * 2.3rem) !important; margin: 0 !important; } .chromecastVolumeRocker { grid-column: 1 / 3; place-items: center; width: calc(100% - (var(--sz) * 1.2rem) - (var(--sz) * 0.4rem * 2)); display: grid; grid-template-columns: 1fr 1fr; } .chromecastVolumeRocker > button { height: calc(var(--sz) * 2rem) !important; width: 100% !important; box-shadow: rgb(0 0 0 / 5%) 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.1428rem) 0; } .chromecastVolumeRocker #volume-down-button { border-radius: calc(var(--sz) * 2rem) 0 0 calc(var(--sz) * 2rem); border-right: calc(var(--sz) * 0.05rem) solid #ebebeb; } .chromecastVolumeRocker #volume-up-button { border-radius: 0% calc(var(--sz) * 2rem) calc(var(--sz) * 2rem) 0; border-left: 0; } .digit-pad-button { font-size: calc(var(--sz) * 1rem); font-weight: 600; height: calc(var(--sz) * 2rem); border-radius: calc(var(--sz) * 1em); } .digit-pad-button:active { font-size: calc(var(--sz) * 0.9rem); } .deviceNameTop { white-space: nowrap; font-size: calc(var(--sz) * 1rem); overflow: hidden; color: var(--devicenamecolor); margin-left: -1rem; text-align: center; display: grid; height: calc(var(--sz) * 2rem); align-items: center; align-content: center; justify-items: center; } .AF6 .deviceNameTop, .AFJTV .deviceNameTop, .AFXF2 .deviceNameTop{ grid-column: 1 / 4; display: block; text-align: center; margin: 0; padding: 0; } .AL1 .deviceNameTop, .AL2 .deviceNameTop { margin: 0rem 0px calc(var(--sz) * 1.25rem); display: block; height: unset; overflow: unset; font-size: calc(var(--sz) * 1.25rem); } .roku-remote-body .deviceNameTop { margin-left: 0; height: auto; } .roku-remote-body.RSR .deviceNameTop { position: absolute; } .shield-remote-body .deviceNameTop { display: unset; align-items: unset; align-content: unset; justify-items: unset; margin-left: 0; margin-top: -1rem; } .shield-remote-body.ns1-body .deviceNameTop { margin-top: 0rem; margin-bottom: -0.75rem; } .XM2 .deviceNameTop { grid-column: 1 / 4; margin: calc(var(--sz) * -1rem) 0 0; } .chromecast-remote-body .deviceNameTop { grid-column: 1 / 3; margin: unset; margin-top: calc(var(--sz) * 1rem); height: unset; display: block; } .apple-remote-body .deviceNameTop { grid-column: 1 / 3; margin: 0 0 calc(var(--sz) * -1.8rem) 0; } .apple-remote-body.AR1 .deviceNameTop { margin: calc(var(--sz) * -4rem) 0 calc(var(--sz) * -2.5rem) 0; } .deviceNameBottom { grid-column: 1/4; color: var(--devicenamecolor); font-size: calc(var(--sz) * 1.25rem); margin-bottom: calc(var(--sz) * -2.5rem); margin-top: calc(var(--sz) * .5rem); white-space: nowrap; overflow: hidden; width: 100%; text-align: center; align-self: flex-end; } .AF6 .deviceNameBottom { margin-bottom: calc(var(--sz) * -1rem); } .AL1 .deviceNameBottom, .AL2 .deviceNameBottom { margin-bottom: unset; margin-top: calc(var(--sz) * 1.8rem); overflow: unset; } .apple-remote-body .deviceNameBottom { grid-column: 1 / 3; margin-bottom: calc(var(--sz) * -1.25rem); } .shield-remote-body .deviceNameBottom { grid-column: 1 / 3; font-size: calc(var(--sz) * 1rem); margin-top: 0px; margin-bottom: 0px; position: absolute; bottom: calc(var(--sz) * 0.3rem); } .chromecast-remote-body .deviceNameBottom { grid-column: 1 / 3; margin-right: unset; margin-bottom: calc(var(--sz) * 1rem); margin-left: unset; margin-top: calc(var(--sz) * 0.5rem); height: unset; display: block; } .roku-remote-body.RTR .deviceNameBottom { margin-bottom: calc(var(--sz) * 0.1rem); margin-top: calc(var(--sz) * -1.25rem); } .roku-remote-body.RWR .deviceNameBottom, .roku-remote-body.RHR .deviceNameBottom { position: absolute; width: calc(var(--sz) * 11.45rem); bottom: calc(var(--sz) * 3.5rem); } .firemoteVersionNumber { text-shadow: 0px 1px 0px #1e1e1e, 0px -1px 0px #000; color: #4e4e4e; align-self: right; text-align: right; width: 100%; justify-self: right; display: block !important; font-size: calc(var(--sz) * .9rem); position: absolute; bottom: calc(var(--sz) * .35rem); right: calc(var(--sz) * .4rem); } .customLauncherBackground { pointer-events: none; position: absolute; height: 100%; width: 100%; transform: scale(1.25); z-index: 1; overflow: hidden; } .customLauncherTxt { z-index: 2; pointer-events: none; } .customLauncherIcon { display: flex; max-width: 100%; max-height: 100%; transform: scale(var(--sz)); display: flex; align-items: center; z-index: 2; } .customLauncherImg { pointer-events: none; max-width: 100%; max-height: 100%; overflow: hidden; z-index: 2; } .AFJTV #tv-button, .AFXF2 #tv-button{ font-size: calc(var(--sz) * 0.8rem); font-weight: 600; } .colorButtons { display: flex; justify-content: space-around; margin: calc(var(--sz) * 0.5rem) 0 calc(var(--sz) * 4.5rem) 0;; } .AFJTV div.colorButtons button, .AFXF2 div.colorButtons button{ border-radius: 100%; overflow: hidden; height: calc(var(--sz) * 2.3rem); width: calc(var(--sz) * 2.3rem); } .AFJTV div.colorButtons button svg, .AFXF2 div.colorButtons button svg { height: calc(var(--sz) * 1.1rem); width: calc(var(--sz) * 1.1rem); filter: brightness(0.8) saturate(0.8); pointer-events: none; } .AFJTV div.colorButtons button:active svg, .AFXF2 div.colorButtons button:active svg{ filter: none; transform: scale(0.85) } .AFJTV .jvcbrandlogo { height: 0; display: flex; justify-content: center; align-items: center; margin-top: calc(var(--sz) * -2rem); } .AFJTV .jvcbrandlogo svg { width: calc(var(--sz) * 5rem); height: auto; } .AFXF2 .xf2brandlogo { height: 0; display: flex; justify-content: center; align-items: center; margin-top: calc(var(--sz) * -1.5rem); } .AFXF2 .xf2brandlogo svg { width: calc(var(--sz) * 7rem); height: auto; } /* Homatics styles */ .homatics-remote-body { align-content: start; grid-row-gap: calc(var(--sz)* 1rem); background: #ebebea; border-color: #dcdcdc; border-radius: calc(var(--sz)* 2.8rem); min-height: calc(var(--sz)* 48.5rem); padding: calc(var(--sz) * 1.1rem) calc(var(--sz) * 0.714rem) calc(var(--sz) * 2.143rem) calc(var(--sz) * 0.714rem); box-shadow: 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.214rem) rgb(0 0 0 / 10%); } .homatics-remote-body.HO2, .homatics-remote-body.HO4 { background: #252525; border-color: #2f2f2f; } .HO1 .remote-button, .HO3 .remote-button { background: #fff; color: rgb(106, 106, 106); border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); box-shadow: rgb(0 0 0 / 15%) 0px calc(var(--sz)* 0.063rem) calc(var(--sz)* 0.188rem); } .HO2 .remote-button, .HO4 .remote-button { background: #2f2f2f; border: solid rgb(15 15 15) calc(var(--sz)* 0.0714rem); } .homatics-remote-body .remote-button:active { box-shadow: inset rgb(0 0 0 / 42%) 0px calc(var(--sz)* 0.1rem) calc(var(--sz)* 0.188rem); filter: brightness(0.94); border: solid rgb(232 232 232) calc(var(--sz)* 0.0714rem); } .HO2 .remote-button:active, .HO4 .remote-button:active { border: solid rgb(10 10 10) calc(var(--sz)* 0.0714rem); } .HO2 .litbutton, .HO4 .litbutton { box-shadow: 0 0 calc(var(--sz)* 0.857rem) calc(var(--sz)* 0.0714rem) rgb(171 253 255 / 15%); } .HO1 .litbutton > ha-icon, .HO3 .litbutton > ha-icon { color: #00979b !important; } .HO2 .litbutton > ha-icon, .HO4 .litbutton > ha-icon { color: #abfdff !important } .homatics-remote-body .deviceNameTop { grid-column: 1 / 4; margin: unset; height: unset; display: block; margin-bottom: calc(var(--sz) * -1rem); } .HO1 #home-button.litbutton > ha-icon, .HO3 #home-button.litbutton > ha-icon { color: #03585b !important } .HO2 #home-button.litbutton > ha-icon, .HO4 #home-button.litbutton > ha-icon { color: #45989a !important } .homatics-remote-body .micHole { background: black; border-radius: 100%; height: calc(var(--sz)* 0.3rem); aspect-ratio: 1 / 1; align-self: end; justify-self: center; } .homatics-remote-body #power-button, .homatics-remote-body #input-button { height: calc(var(--sz)* 2.75rem); width: calc(var(--sz)* 2.75rem); } .remote-body.HO1 .remote-button > ha-icon, .remote-body.HO3 .remote-button > ha-icon { color: rgb(106 106 106); } .remote-body.HO2 .remote-button > ha-icon { color: rgb(245 245 245); } .homatics-remote-body .remote-button.dark { background: linear-gradient(180deg, rgb(168 168 168) 26%, rgb(144 144 144) 50%, rgb(128 128 128) 75%); } .homatics-remote-body.HO2 .remote-button.dark, .homatics-remote-body.HO4 .remote-button.dark { background: linear-gradient(180deg, rgb(255 255 255) 26%, rgb(221 221 221) 50%, rgb(211 211 211) 75%); } .homatics-remote-body .remote-button.dark > ha-icon { color: #000; } .homatics-remote-body.HO2 .remote-button.dark > ha-icon, .homatics-remote-body.HO4 .remote-button.dark > ha-icon { color: #4b4b4b; } .remote-body.HO2 .remote-button.light > ha-icon { color: #464646; } .homatics-remote-body .micNLight { display: grid; align-content: space-between; padding: calc(var(--sz) * 0.25rem) 0; } .homatics-remote-body .activityLight { background: #ff0000; box-shadow: #ff0000 0 0 calc(var(--sz)* 0.6rem) calc(var(--sz)* 0.05rem); border-radius: 100%; height: calc(var(--sz)* 0.4rem); aspect-ratio: 1 / 1; opacity: 0; } .homatics-remote-body.HO2 .activityLight, .homatics-remote-body.HO4 .activityLight { background: #adff87; box-shadow: lime 0 0 calc(var(--sz)* 0.6rem) calc(var(--sz)* 0.05rem); } .homatics-remote-body .digit-pad-button { font-size: calc(var(--sz)* 1rem); font-weight: 600; height: calc(var(--sz)* 2.75rem); width: calc(var(--sz)* 2.75rem); border-radius: 100%; margin-bottom: calc(var(--sz)* -0.6rem); } .homatics-remote-body .colorButtons { display: flex; justify-content: space-around; margin: calc(var(--sz) * 0.5rem) 0 0 0; } .homatics-remote-body div.colorButtons button { border-radius: 100%; overflow: hidden; height: calc(var(--sz) * 2.3rem); width: calc(var(--sz) * 2.3rem); } .homatics-remote-body div.colorButtons button svg{ height: calc(var(--sz) * 1.1rem); width: calc(var(--sz) * 1.1rem); filter: brightness(0.8) saturate(0.8); pointer-events: none; } .homatics-remote-body div.colorButtons button:active svg{ filter: none; transform: scale(0.85) } .homatics-remote-body #bookmark-button, .homatics-remote-body #settings-button { align-self: end; } .homatics-remote-body #keyboard-button { height: calc(var(--sz)* 3.572rem); width: calc(var(--sz)* 3.572rem); margin-bottom: calc(var(--sz)* 1.1rem); } .homatics-remote-body .directionButtonContainer { box-shadow: rgb(0 0 0 / 15%) calc(var(--sz)* 0.025rem) calc(var(--sz)* 0.025rem) calc(var(--sz)* 0.025rem) background: #cacaca; border: calc(var(--sz)* 0.0714rem) solid #cacaca; } .homatics-remote-body .directionButtonContainer:has(#down-button:active) { box-shadow: none; } .homatics-remote-body .dpadContainer { align-items: center; justify-items: center; position: relative; margin-top: calc(var(--sz)* -1.5rem); margin-bottom: calc(var(--sz)* -1.5rem); isolation: isolate; } .homatics-remote-body .dpadbutton, .homatics-remote-body .dpadbutton:active { all: unset; cursor: pointer; width: calc(var(--sz)* 5.5714rem); height: calc(var(--sz)* 5.5714rem); outline: solid #a6a6a6 calc(var(--sz)* 0.0714rem); } .homatics-remote-body .dpadbutton { background: #adadad; appearance: none; -webkit-appearance: none; -moz-appearance: none; -webkit-tap-highlight-color: transparent; } .HO2 .dpadbutton, .HO4 .dpadbutton { background: rgb(216 216 216); outline: solid rgb(200 200 200) calc(var(--sz)* 0.0714rem); } .homatics-remote-body .dpadbutton:active { background: rgb(153 153 153); box-shadow: inset #4f4f4f 0 0 calc(var(--sz)* 0.35rem) 0px; transform: none; filter: none; overflow: hidden; backdrop-filter: none; appearance: none; } .HO2 .dpadbutton:active, .HO4 .dpadbutton:active { background: rgb(200 200 200); box-shadow: none; outline: solid #989898 calc(var(--sz)* 0.0714rem); } .homatics-remote-body .centerbutton { background: linear-gradient(180deg, #c8c8c8 0%, rgba(255, 255, 255, 1) 100%); border: solid #a1a1a1 calc(var(--sz)* 0.25rem); width: calc(var(--sz)* 5.13rem); height: calc(var(--sz)* 5.13rem); margin: 0px; position: absolute; box-sizing: border-box; isolation: isolate; } .HO2 .centerbutton, .HO4 .centerbutton { background: linear-gradient(0deg, rgb(48 48 48) 26%, rgb(39 39 39) 50%, rgb(33 33 33) 75%); border: solid #252525 calc(var(--sz)* 0.25rem); } .homatics-remote-body .centerbutton:active { transform: none; filter: brightness(0.92); border: solid #b3b3b3 calc(var(--sz)* 0.29rem); } .HO2 .centerbutton:active, .HO4 .centerbutton:active { filter: brightness(0.8); border: solid #000 calc(var(--sz)* 0.29rem); } .homatics-remote-body #home-button { align-self: end; margin-top: calc(var(--sz)* 1.1rem); z-index: 10; /* Required for companion app to prevent accidental push of down button */ } .homatics-remote-body .volumeChannelSection { grid-column-start: 1; grid-column-end: 4; display: grid; grid-template-columns: 33% 1fr 33%; grid-template-areas: "vol-up mute ch-up" "vol-down mute ch-down"; grid-row-gap: 0; width: 100%; justify-items: center; align-items: center; } .homatics-remote-body #volume-up-button-container { grid-area: vol-up; margin-bottom: 0; border-bottom: 0; font-size: calc(var(--sz)* 2rem); } .homatics-remote-body #volume-up-button-container ha-icon, .homatics-remote-body #channel-up-button-container ha-icon { margin-top: calc(var(--sz)* -1rem); } .homatics-remote-body #voltext, .homatics-remote-body #chtext { max-width: calc(var(--sz)* 1.5rem); max-height: calc(var(--sz)* 1rem); position: absolute; padding-left: calc(var(--sz)* 1.0rem); pointer-events: none; margin-top: calc(var(--sz)* -0.5rem); } .homatics-remote-body #voltext > * { fill: rgb(106 106 106); } .homatics-remote-body.HO2 #voltext > *, .homatics-remote-body.HO4 #voltext > * { fill: rgb(245, 245, 245); } .homatics-remote-body #chtext { max-width: calc(var(--sz)* 1rem); padding-left: calc(var(--sz)* 1.25rem); } .homatics-remote-body #chtext > * { fill: rgb(106 106 106); } .homatics-remote-body.HO2 #chtext > *, .homatics-remote-body.HO4 #chtext > * { fill: rgb(245, 245, 245); } .homatics-remote-body #channel-up-button-container { grid-area: ch-up; margin-bottom: 0; border-bottom: 0; } .homatics-remote-body #mute-button { grid-area: mute; height: calc(var(--sz)* 2.6rem); width: calc(var(--sz)* 2.6rem); } #soundeqtext { grid-area: mute; max-width: calc(var(--sz)* 3.5rem); max-height: calc(var(--sz)* 0.7rem); margin-top: calc(var(--sz)* 4.1rem); fill: rgb(106 106 106); } .HO2 #soundeqtext, .HO4 #soundeqtext { fill: rgb(245, 245, 245); } .homatics-remote-body #volume-down-button { grid-area: vol-down; border-top: 0; font-size: calc(var(--sz)* 2rem); } .homatics-remote-body #volume-down-button ha-icon, .homatics-remote-body #channel-down-button ha-icon { margin-bottom: calc(var(--sz)* -1rem); } .homatics-remote-body #channel-down-button { grid-area: ch-down; border-top: 0; } .homatics-remote-body #volume-down-button, .homatics-remote-body #channel-down-button { box-shadow: rgb(0 0 0 / 15%) 0px calc(var(--sz)* 0.063rem) calc(var(--sz)* 0.1rem); } .homatics-remote-body #volume-up-button, .homatics-remote-body #channel-up-button { box-shadow: none; } .homatics-remote-body #volume-up-button:active, .homatics-remote-body #channel-up-button:active { filter: none; background: linear-gradient(0deg, rgba(255, 255, 255, 1) 0%, rgba(240, 240, 240, 1) 95%); border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); border-bottom: 0; } .homatics-remote-body.HO2 #volume-up-button:active, .homatics-remote-body.HO2 #channel-up-button:active, .homatics-remote-body.HO4 #volume-up-button:active, .homatics-remote-body.HO4 #channel-up-button:active { filter: none; background: linear-gradient(0deg, rgb(46 46 46) 0%, rgb(38 38 38) 95%); border: solid rgb(0 0 0) calc(var(--sz)* 0.08rem); border-bottom: 0; } .homatics-remote-body #volume-down-button:active, .homatics-remote-body #channel-down-button:active { filter: none; background: linear-gradient(0deg, rgba(240, 240, 240, 1) 0%, rgba(255, 255, 255, 1) 95%); box-shadow: none; border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); border-top: 0; } .homatics-remote-body.HO2 #volume-down-button:active, .homatics-remote-body.HO2 #channel-down-button:active, .homatics-remote-body.HO4 #volume-down-button:active, .homatics-remote-body.HO4 #channel-down-button:active { filter: none; background: linear-gradient(180deg, rgb(46 46 46) 0%, rgb(38 38 38) 95%); box-shadow: none; border: solid rgb(0 0 0) calc(var(--sz)* 0.08rem); border-top: 0; } .homatics-remote-body .afappsgrid { margin-bottom: calc(var(--sz)* 7rem); } .homatics-remote-body .srcButton { border: solid #a7a7a7 calc(var(--sz) * 0.0714rem); } .homaticsLogo { position: absolute; bottom: calc(var(--sz)* 3rem); width: calc(var(--sz)* 12.286rem); text-align: center; } .homaticsLogo svg { max-width: calc(var(--sz)* 6.5rem); max-height: calc(var(--sz)* 3.2rem); stroke: rgb(161 161 161); } .HO2 .homaticsLogo svg, .HO4 .homaticsLogo svg { stroke: #000; } .homatics-remote-body .deviceNameBottom { position: absolute; bottom: calc(var(--sz)* 3.5rem); width: calc(var(--sz)* 12.286rem); text-align: center; } .homatics-remote-body .srcButton { border-color: rgb(255 255 255 / 0%); } .homatics-remote-body .srcButton.appActive { box-shadow: rgb(0 0 0 / 13%) 0 calc(var(--sz)* 0.214rem) calc(var(--sz)* 0.1428rem) 0, white 0 0 calc(var(--sz)* 0.857rem) calc(var(--sz)* 0.429rem); border-color: #e6e6e6; animation-name: homatics_flash_border; animation-duration: 2s; animation-timing-function: linear; animation-iteration-count: infinite; } .HO2 .srcButton.appActive, .HO4 .srcButton.appActive { box-shadow: rgb(255 255 255 / 0%) 0 calc(var(--sz)* 0.214rem) calc(var(--sz)* 0.1428rem) 0, rgb(255 255 255 / 31%) 0 0 calc(var(--sz)* 0.857rem) calc(var(--sz)* 0.429rem); } @keyframes homatics_flash_border { 0% { border-color: rgb(0 0 0 / 16%); } 50% { border-color: #fff; } 100% { border-color: rgb(0 0 0 / 16%); } } /* end Homatics styles */ /* onn. styles */ .remote-body.ON1, .remote-body.ON2 { align-content: start; grid-row-gap: calc(var(--sz)* 1rem); background: #ebebea; border-color: #dcdcdc; border-radius: calc(var(--sz)* 2.8rem); min-height: calc(var(--sz)* 48.5rem); padding: calc(var(--sz) * 1.1rem) calc(var(--sz) * 0.714rem) calc(var(--sz) * 2.143rem) calc(var(--sz) * 0.714rem); box-shadow: 0 calc(var(--sz) * 0.214rem) calc(var(--sz) * 0.214rem) rgb(0 0 0 / 10%); } .remote-body.ON2 { background: #252525; border-color: #2f2f2f; } .ON1 .remote-button, .ON2 .remote-button { background: #fff; color: rgb(106, 106, 106); border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); box-shadow: rgb(0 0 0 / 15%) 0px calc(var(--sz)* 0.063rem) calc(var(--sz)* 0.188rem); } .ON2 .remote-button { background: #2f2f2f; border: solid rgb(15 15 15) calc(var(--sz)* 0.0714rem); } .ON1 .remote-button:active, .ON2 .remote-button:active { box-shadow: inset rgb(0 0 0 / 42%) 0px calc(var(--sz)* 0.1rem) calc(var(--sz)* 0.188rem); filter: brightness(0.94); border: solid rgb(232 232 232) calc(var(--sz)* 0.0714rem); } .ON2 .remote-button:active { border: solid rgb(10 10 10) calc(var(--sz)* 0.0714rem); } .remote-body.ON1 .remote-button.litbutton { background: #ffffe7; } .remote-body.ON1 #power-button.litbutton > ha-icon, .remote-body.ON2 .remote-button.litbutton > ha-icon { color: #017374 !important; } .remote-body.ON2 #power-button.litbutton > ha-icon { color: #ebef66 !important; } .remote-body.ON1 .remote-button:active > ha-icon, .remote-body.ON2 .remote-button:active > ha-icon { transform: scale(calc(var(--sz)* 0.90)); } .remote-body.ON1 .remote-button.dark, .remote-body.ON2 .remote-button.dark { background: linear-gradient(180deg, rgba(69,69,69,1) 26%, rgba(55,55,55,1) 50%, rgba(45,45,45,1) 75%); } .remote-body.ON2 .remote-button.light { background: linear-gradient(0deg, #c8c8c8 0%, rgba(255, 255, 255, 1) 100%); } .remote-body.ON1 .remote-button > ha-icon { color: rgb(106 106 106); } .remote-body.ON2 .remote-button > ha-icon { color: rgb(245 245 245); } .remote-body.ON1 .remote-button.dark > ha-icon { color: #ebebea; } .remote-body.ON2 .remote-button.light > ha-icon { color: #464646; } .remote-body.ON1 .remote-button.dark.litbutton { background: #004040; } .remote-body.ON1 .remote-button.litbutton > ha-icon, .remote-body.ON1 .remote-button.dark.litbutton > ha-icon, .remote-body.ON2 .remote-button.dark.litbutton > ha-icon { color: #ffffab; } .ON1 .directionButtonContainer, .ON2 .directionButtonContainer { box-shadow: rgb(0 0 0 / 73%) calc(var(--sz)* 0.025rem) calc(var(--sz)* 0.025rem) calc(var(--sz)* 0.025rem); background: black; } .ON1 .directionButtonContainer:has(#down-button:active), .ON2 .directionButtonContainer:has(#down-button:active) { box-shadow: none; } .ON1 .dpadContainer, .ON2 .dpadContainer { align-items: center; justify-items: center; position: relative; margin-top: calc(var(--sz)* -1.5rem); margin-bottom: calc(var(--sz)* -1.5rem); isolation: isolate; } .ON1 .dpadbutton, .ON1 .dpadbutton:active, .ON2 .dpadbutton, .ON2 .dpadbutton:active { all: unset; cursor: pointer; width: calc(var(--sz)* 5.5714rem); height: calc(var(--sz)* 5.5714rem); outline: solid #2e2e2e calc(var(--sz)* 0.0714rem); } .ON1 .dpadbutton, .ON2 .dpadbutton { background: rgba(55, 55, 55, 1); appearance: none; -webkit-appearance: none; -moz-appearance: none; -webkit-tap-highlight-color: transparent; } .ON2 .dpadbutton { background: rgb(216 216 216); outline: solid rgb(200 200 200) calc(var(--sz)* 0.0714rem); } .ON1 .dpadbutton:active, .ON2 .dpadbutton:active { background: rgba(45, 45, 45, 1); box-shadow: inset #161616 0 0 calc(var(--sz)* 0.35rem) 0px; transform: none; filter: none; overflow: hidden; backdrop-filter: none; appearance: none; } .ON2 .dpadbutton:active { background: rgb(200 200 200); box-shadow: none; outline: solid #989898 calc(var(--sz)* 0.0714rem); } .ON1 .centerbutton, .ON2 .centerbutton { background: linear-gradient(180deg, #c8c8c8 0%, rgba(255, 255, 255, 1) 100%); border: solid #2b2b2b calc(var(--sz)* 0.25rem); width: calc(var(--sz)* 5.13rem); height: calc(var(--sz)* 5.13rem); margin: 0px; position: absolute; box-sizing: border-box; isolation: isolate; } .ON2 .centerbutton { background: linear-gradient(0deg, rgb(48 48 48) 26%, rgb(39 39 39) 50%, rgb(33 33 33) 75%); } .ON1 .centerbutton:active, .ON2 .centerbutton:active { transform: none; filter: brightness(0.92); border: solid #2b2b2b calc(var(--sz)* 0.29rem); } .ON2 .centerbutton:active { filter: brightness(0.8); } .ON1 #power-button, .ON1 #input-button, .ON2 #power-button, .ON2 #magic-star-button { height: calc(var(--sz)* 2.5rem); width: calc(var(--sz)* 2.5rem); } .ON1 #power-button, .ON2 #power-button { justify-self: left; } .ON1 .micNLight, .ON2 .micNLight { display: grid; align-content: space-between; padding: calc(var(--sz) * 0.25rem) 0; } .ON1 .activityLight, .ON2 .activityLight { background: #adff87; box-shadow: lime 0 0 calc(var(--sz)* 0.6rem) calc(var(--sz)* 0.05rem); border-radius: 100%; height: calc(var(--sz)* 0.4rem); aspect-ratio: 1 / 1; opacity: 0; } .ON1 .micHole, .ON2 .micHole { background: black; border-radius: 100%; height: calc(var(--sz)* 0.3rem); aspect-ratio: 1 / 1; align-self: end; justify-self: center; } .ON1 #input-button, .ON2 #magic-star-button { justify-self: right; } .ON1 #profile-button, .ON1 #settings-button, .ON2 #profile-button, .ON2 #settings-button { align-self: end; } .ON1 #keyboard-button, .ON2 #keyboard-button { height: calc(var(--sz)* 3.572rem); width: calc(var(--sz)* 3.572rem); margin-bottom: calc(var(--sz)* 1.1rem); } .ON1 #home-button, .ON2 #home-button { align-self: end; margin-top: calc(var(--sz)* 1.1rem); z-index: 10; /* Required for companion app to prevent accidental push of down button */ } .ON1 .volumeChannelSection, .ON2 .volumeChannelSection { grid-column-start: 1; grid-column-end: 4; display: grid; grid-template-columns: 33% 1fr 33%; grid-template-areas: "vol-up mute ch-up" "vol-down mute ch-down"; grid-row-gap: 0; width: 100%; justify-items: center; align-items: center; } .ON1 #volume-up-button, .ON2 #volume-up-button { grid-area: vol-up; margin-bottom: 0; border-bottom: 0; font-size: calc(var(--sz)* 2rem); } .ON1 #channel-up-button, .ON2 #channel-up-button { grid-area: ch-up; margin-bottom: 0; border-bottom: 0; } .ON1 #mute-button, .ON2 #mute-button { grid-area: mute; height: calc(var(--sz)* 2.6rem); width: calc(var(--sz)* 2.6rem); } .ON1 #volume-down-button, .ON2 #volume-down-button { grid-area: vol-down; border-top: 0; font-size: calc(var(--sz)* 2rem); } .ON1 #channel-down-button, .ON2 #channel-down-button { grid-area: ch-down; border-top: 0; } .ON1 #volume-up-button:active, .ON1 #channel-up-button:active { filter: none; background: linear-gradient(0deg, rgba(255, 255, 255, 1) 0%, rgba(240, 240, 240, 1) 95%); border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); border-bottom: 0; } .ON2 #volume-up-button:active, .ON2 #channel-up-button:active { filter: none; background: linear-gradient(0deg, rgb(46 46 46) 0%, rgb(38 38 38) 95%); border-bottom: 0; } .ON1 #volume-down-button:active, .ON1 #channel-down-button:active { filter: none; background: linear-gradient(0deg, rgba(240, 240, 240, 1) 0%, rgba(255, 255, 255, 1) 95%); box-shadow: none; border: solid rgb(186 186 186) calc(var(--sz)* 0.0714rem); border-top: 0; } .ON2 #volume-down-button:active, .ON2 #channel-down-button:active { filter: none; background: linear-gradient(180deg, rgb(46 46 46) 0%, rgb(38 38 38) 95%); box-shadow: none; border-top: 0; } .ON1 .afappsgrid, .ON2 .afappsgrid { align-content: start; margin: calc(var(--sz)* 0.75rem) 0 calc(var(--sz)* 3.75rem) 0; } .ON1 .srcButton { border-color: #ccc; } .ON2 .srcButton { border-color: #292929; } .ON1 .srcButton.appActive, .ON2 .srcButton.appActive { box-shadow: rgb(0 0 0 / 13%) 0 calc(var(--sz)* 0.214rem) calc(var(--sz)* 0.1428rem) 0, white 0 0 calc(var(--sz)* 0.857rem) calc(var(--sz)* 0.429rem); border-color: #e6e6e6; animation-name: flash_border; animation-duration: 2s; animation-timing-function: linear; animation-iteration-count: infinite; } .ON2 .srcButton.appActive { box-shadow: rgb(255 255 255 / 0%) 0 calc(var(--sz)* 0.214rem) calc(var(--sz)* 0.1428rem) 0, rgb(255 255 255 / 31%) 0 0 calc(var(--sz)* 0.857rem) calc(var(--sz)* 0.429rem); } @keyframes flash_border { 0% { border-color: rgb(0 0 0 / 16%); } 50% { border-color: #fff; } 100% { border-color: rgb(0 0 0 / 16%); } } .ON1 .srcButton:active, .ON2 .srcButton:active { box-shadow: inset rgb(0 0 0 / 25%) 0 calc(var(--sz)* 0.214rem) calc(var(--sz)* 0.1428rem) 0; } .ON1 .deviceNameTop, .ON2 .deviceNameTop { grid-column: 1 / 4; margin: calc(var(--sz)* -1rem) 0; } .ON1 .deviceNameBottom, .ON2 .deviceNameBottom { position: absolute; bottom: calc(var(--sz)* 0.75rem); margin: 0; width: calc(var(--sz)* 12.286rem); text-align: center; } .onnLogo { position: absolute; bottom: calc(var(--sz)* 2rem); width: calc(var(--sz)* 12.286rem); text-align: center; } .onnLogo svg { max-width: calc(var(--sz)* 5rem); max-height: calc(var(--sz)* 2rem); } .ON2 .onnLogo svg { fill: #949494; } /* end onn. styles */ ${unsafeCSS(launcherCSS)} .functionFindRemoteButton, .functionMuteButton, .functionRebootButton, .functionSystemUpdateButton, .functionKeyboardButton, .functionMenuButton, .functionSettingsButton { color: #ff0000; font-weight: bold; background: #000; border: solid calc(var(--sz) * 0.1rem) #850000; display: block; } .functionAppSwitchButton, .functionAppManageButton { background: #000; } .ON1 .functionRebootButton, .ON2 .functionRebootButton { background: #fff; } .ON1 .functionSearchGoogleTVButton, .ON1 .functionCaptionsButton, .ON1 .functionpairingButton, .ON2 .functionSearchGoogleTVButton, .ON2 .functionCaptionsButton, .ON2 .functionpairingButton { background: #fff; color: #000; } .roku-remote-body .functionMuteButton, .roku-remote-body .functionVolumeUpButton, .roku-remote-body .functionVolumeDownButton, .roku-remote-body .functionFindRemoteButton, .roku-remote-body .searchButton, .roku-remote-body .functionChannelUpButton, .roku-remote-body .functionChannelDownButton { color: #c6c6c6; font-weight: bold; background: #662d91; border: 0; filter: none !important; display: grid; } .functionAppleSettingsButton { background: linear-gradient(0deg, rgba(143,143,148,1) 0%, rgba(228,228,233,1) 100%); border: solid #838383 calc(var(--sz) * 0.05rem) !important; } .functionKeyboardButton { display: inherit; } .functionFindRemoteButton > ha-icon, .functionMuteButton > ha-icon, .functionRebootButton > ha-icon, .functionSettingsButton > ha-icon { display: none; } .ON1 .functionSystemUpdateButton, .ON2 .functionSystemUpdateButton { background: #fff; color: #000; } .functionAppSwitchButton:active, .functionFindRemoteButton:active, .functionMuteButton:active, .functionRebootButton:active, .functionSettingsButton:active { filter: none; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.142rem) rgb(255 255 255 / 20%) !important; } .ON1 .functionFindRemoteButton, .ON2 .functionFindRemoteButton { color: yellow; border: solid calc(var(--sz) * 0.1rem) #017374; } .shield-remote-body .functionFindRemoteButton, .shield-remote-body .functionMuteButton, .shield-remote-body .functionSystemUpdateButton, .shield-remote-body .functionMuteButton, .shield-remote-body .functionRebootButton, .shield-remote-body .functionSettingsButton, .shield-remote-body .functionCaptionsButton { color: rgb(153, 231, 0); border: solid calc(var(--sz) * 0.1rem) #456800; } .functionFindRemoteButton { font-size: calc(var(--sz) * .75rem); } .shield-remote-body .functionFindRemoteButton { font-size: calc(var(--sz) * .75rem); --mdc-icon-size: calc(var(--sz) * 1.4rem); } .roku-remote-body .functionFindRemoteButton { font-size: calc(var(--sz) * .6rem); } .functionMuteButton { font-size: calc(var(--sz) * 1rem); } .shield-remote-body .functionMuteButton { font-size: calc(var(--sz) * 1.2rem); --mdc-icon-size: calc(var(--sz) * 1.6rem); } .functionRebootButton { font-size: calc(var(--sz) * 1rem); } .shield-remote-body .functionRebootButton { font-size: calc(var(--sz) * 1rem); --mdc-icon-size: calc(var(--sz) * 1.6rem); } .chromecast-remote-body .functionRebootButton { font-size: calc(var(--sz) * 0.95rem); background: #fff; } .apple-remote-body .functionAppSwitchButton { background: rgb(33, 33, 33); } .functionSettingsButton { font-size: calc(var(--sz) * .75rem); } .shield-remote-body .functionSettingsButton { font-size: calc(var(--sz) * 0.86rem); --mdc-icon-size: calc(var(--sz) * 1.5rem); } .chromecast-remote-body .functionSettingsButton, .chromecast-remote-body .functionSystemUpdateButton { font-size: calc(var(--sz) * 0.7rem); background: #fff; } .functionControlCenterButton { background: rgb(215 215 215); border: solid #838383 calc(var(--sz) * 0.05rem) !important; } .remote-logo { grid-column-start: 1; grid-column-end: 4; padding: calc(var(--sz) * 2.5rem) calc(var(--sz) * 2.357rem) 0 calc(var(--sz) * 2.357rem); width: calc(var(--sz) * 7.5714rem); } .AF6 .remote-logo { padding: calc(var(--sz) * .75rem) calc(var(--sz) * 2.357rem) 0 calc(var(--sz) * 2.357rem); } .miLogo, .xiaomiLogo { align-self: flex-end; padding-bottom: calc(var(--sz) * 1.3rem) } .miLogo { width: calc(var(--sz) * 2.75rem); } .ns1-body #keyboard-button { margin-top: calc(var(--sz) * 1rem); height: calc(var(--sz) * 5rem); width: calc(var(--sz) * 5rem); --mdc-icon-size: 34px; } .ns1-body .remote-button:active { border: solid #395600 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(153 231 0 / 20%); } .ns1-body .remote-button:active > ha-icon { color: #99e700 !important; } .litbutton { border: solid #4b4c3c 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 255 25 / 15%); } .litbutton > ha-icon { color: yellow !important; } .shield-remote-body .litbutton { border: solid #500101 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 25 25 / 15%); } .shield-remote-body .litbutton > ha-icon, .roku-remote-body #power-button.litbutton > ha-icon { color: red !important; } .roku-remote-body #power-button.litbutton { border: 0.0714rem solid rgb(92 0 0); box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(229 58 58 / 31%); } .roku-remote-body.RTR #power-button { background: #810000; } .roku-remote-body.RTR #power-button > ha-icon { color: #c6c6c6; } .roku-remote-body.RTR #power-button.litbutton { background: rgb(159, 0, 0); border: 0.0714rem solid rgb(139, 62, 62); box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(229 164 164 / 31%); } .roku-remote-body.RTR #power-button.litbutton > ha-icon { color: #fff !important; } .roku-remote-body #home-button.litbutton, .roku-remote-body #playpause-button.litbutton { box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 255 255 / 20%); } .roku-remote-body #home-button.litbutton > ha-icon, .roku-remote-body #playpause-button.litbutton > ha-icon { color: white !important; } .XM2 .litbutton { border: solid #5c2b00 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(229 124 58 / 31%); } .XM2 .litbutton > ha-icon, .XM2 .litbutton > .mdiSubstituteIconWrapper { color: #ff7700 !important; } .chromecast-remote-body .litbutton, .chromecast-remote-body .appActive { position: relative; z-index: 2; } .chromecast-remote-body .appActive { border: transparent; } .chromecast-remote-body .litbutton::before, .chromecast-remote-body .appActive::before { content: ""; position: absolute; z-index: -1; inset: 0.25rem; background: conic-gradient(from 0deg, rgba(66,133,244,1) 0%, rgba(219,68,55,1) 25%, rgba(244,180,0,1) 50%, rgba(15,157,88,1) 75%, rgba(66,133,244,1) 100%); filter: blur(0.3rem) contrast(3); transition: opacity 0.3s ease 0s; border-radius: inherit; animation: spin 7s linear infinite; } .chromecast-remote-body .appActive::before { inset: calc(var(--sz) * -0.25rem); filter: contrast(3); } @keyframes spin { 0 { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .chromecast-remote-body .litbutton::after, .chromecast-remote-body .appActive::after { content: ""; z-index: -1; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: inherit; border-radius: inherit; inset: 0.15rem; } .chromecast-remote-body .litbutton > ha-icon { color: #d70000 !important; } .apple-remote-body .litbutton { background: rgb(255 255 255 / 25%) !important; border: 0.01rem solid gray !important; box-shadow: none; } .apple-remote-body .litbutton > ha-icon { color: rgb(33, 33, 33) !important; } .shield-remote-body.ns1-body .litbutton { border: solid #395600 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(153 231 0 / 20%); } .shield-remote-body.ns1-body .litbutton > ha-icon { color: #99e700 !important; } .dimlitbutton { border: solid #34342b calc(var(--sz) * 0.0714rem); box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 255 116 / 15%); } .dimlitbutton > ha-icon { color: #e5e59a !important; } .apple-remote-body .dimlitbutton { border: 0.01rem solid gray !important; background: none !important; box-shadow: none; } .apple-remote-body .dimlitbutton > ha-icon { color: rgb(33, 33, 33) !important; } .shield-remote-body .dimlitbutton { border: solid #3c1818 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 25 25 / 11%); } .shield-remote-body .dimlitbutton > ha-icon { color: #ff7575 !important; } .XM2 .dimlitbutton { border: solid #463327 0.0714rem; box-shadow: 0 0 calc(var(--sz) * 0.857rem) calc(var(--sz) * 0.0714rem) rgb(255 143 36 / 21%); } .XM2 .dimlitbutton > ha-icon { color: #b36d41 !important; } ha-icon { pointer-events: none; transform: scale(var(--sz)); } .eightygap { height: calc(var(--sz) * 5.7143rem); } `; //` getState() { if(this._config.device_family === 'none' || this._config.entity === 'none') { return; } if(this._config.android_tv_remote_entity == '' || typeof this._config.android_tv_remote_entity == 'undefined' || this._config.device_family == 'amazon-fire' ) { return this.hass.states[this._config.entity]; } else { return this.hass.states[this._config.android_tv_remote_entity]; } } getOpenAppID() { if(this._config.device_family === 'none' || this._config.entity === 'none') { return; } else if(this._config.device_family == 'roku') { return this.hass.states[this._config.entity].attributes.app_name; } else if(this._config.android_tv_remote_entity == '' || typeof this._config.android_tv_remote_entity == 'undefined' || this._config.device_family == 'amazon-fire' ) { return this.hass.states[this._config.entity].attributes.app_id; } else { if(this.getState().state == 'on') { return this.hass.states[this._config.android_tv_remote_entity].attributes.current_activity; } } } createRenderRoot() { const root = super.createRenderRoot(); // This section prevents unwanted scrolling on iOS devices when double clicking in a Sections view #508 root.addEventListener( 'dblclick', (e) => (e.preventDefault()) ); return root; } render() { if (!this.hass || !this._config) { return html``; } const stateObj = this.hass.states[this._config.entity]; if (!stateObj && this._config.entity != 'none') { return html` Unknown entity: ${this._config.entity} `; } const entityId = this._config.entity; const state = this.getState(); const stateStr = state ? state.state : 'off'; const appId = this.getOpenAppID(); const deviceType = this._config.device_type; const scale = (parseInt(this._config.scale) || 100)/100; var launcherscaleoffset = 0; if (this._config.app_launcher_relative_size) { launcherscaleoffset = (parseInt(this._config.app_launcher_relative_size) || 100)/100 }; const launcherscale = scale + launcherscaleoffset; const overrides = this._config.button_overrides; var buttonHidingCss = ''; if(overrides && typeof overrides === 'object') { for (let [key, value] of Object.entries(overrides)) { if(value && typeof value === 'object') { for (let [action, actionvalue] of Object.entries(value)) { if(action == 'hidden' && actionvalue == true) { buttonHidingCss += '#'+key+' { opacity: 0; pointer-events: none; } '; } } } } } var AppLaunchButtonFilterCssValue = 'grayscale(25%) brightness(58%)'; if(this._config.device_family == 'apple-tv') { AppLaunchButtonFilterCssValue = 'grayscale(0%) brightness(100%)'; } else if (['onn', 'homatics'].includes(this._config.device_family) || ['ON1', 'ON2', 'HO1', 'HO2', 'HO3', 'HO4'].includes(this._config.defaultRemoteStyle_override)) { AppLaunchButtonFilterCssValue = 'grayscale(25%) brightness(80%)'; } var guiEditorDirection = this.hass.config.language == 'he' ? 'rtl' : 'ltr'; const devicenamecolor = this._config.visible_name_text_color || '#000000'; var backgroundInherit = ''; if (this._config.use_theme_background == true) { backgroundInherit = 'background: var(--ha-card-background,var(--card-background-color,#fff))!important; border: inherit !important; border-radius: inherit !important;'; } if (this._config.useCustomSkin===true) { var skin = '#4682b4'; if(this._config.skin) { skin = this._config.skin; } backgroundInherit = 'background: '+skin+' !important;'; } const cssVars = html ``; // Handle standard button highlight/lit states var powerStatusClass = '' var homeStatusClass = ''; var playingStatusClass = ''; var patchWallStatusClass = ''; if ((this._config.hide_button_highlights != true)) { // Determine Power On/Off Status if(stateStr != 'off' && stateStr != 'unavailable') { powerStatusClass = ' litbutton'; } if(stateStr == 'standby') { powerStatusClass = ' dimlitbutton'; } // Determine Home Status if(['com.amazon.tv.launcher', 'com.google.android.tvlauncher', 'com.google.android.apps.tv.launcherx', 'Home'].includes(appId)) { homeStatusClass = ' litbutton'; } // Determine Play/Pause var alwaysRegisterdAsPlaying = ['com.amazon.firebat', 'com.android.systemui', 'com.android.vending', 'com.android.tv.settings', 'com.nvidia.shieldtech.accessoryui', 'com.esaba.downloader', 'com.amazon.venezia']; if(stateStr == 'playing' && !(alwaysRegisterdAsPlaying.includes(appId))) { playingStatusClass = ' litbutton'; } // Determine Patchwall Status if(appId == 'com.mitv.tvhome.atv') { patchWallStatusClass = ' litbutton'; } } // Get current device's Attributes AND use any applicable overrides from user conf var confRef = this._config; function getDeviceAttribute(deviceAttribute){ return deviceAttributeQuery(deviceAttribute, confRef); } // Rebuild AppMap - allow hdmi inputs where appropriate & add configured custom launchers from YAML refreshAppMap(this._config, getDeviceAttribute('hdmiInputs'), getDeviceAttribute('avInputs'), getDeviceAttribute('tuner')); // get app button details from appmap json function getAppButtonData(config, configvalue, want) { if(appmap.has(configvalue)) { var deviceFamily = config["device_family"]; var familySpecificAppData = appmap.get(configvalue)[deviceFamily]; if(want=="active") { if (config["device_type"]=="fire_tv_stick_4k_max_second_gen" || config["device_type"]=="fire_tv_stick_4k_second_gen") { return "appActiveUnknown"; } if (typeof appId != 'string') { return }; if(familySpecificAppData && !(appmap.get(configvalue).androidName) && !(appmap.get(configvalue).androidName2) && !(appmap.get(configvalue).appName)) { return (appId == familySpecificAppData["androidName"] || appId == familySpecificAppData["androidName2"] || appId == familySpecificAppData["appName"]) ? "appActive" : ""; } else { return (appId == appmap.get(configvalue).androidName || appId == appmap.get(configvalue).androidName2) || appId == appmap.get(configvalue).appName ? "appActive" : ""; } } else { if (appmap.get(configvalue)[want]) { return appmap.get(configvalue)[want]; } else if(familySpecificAppData) { return familySpecificAppData[want]; } } } else { return ' '; } } function drawAppLaunchButtons(e, config, cols=3, max=6) { var spanclass = "three-col-span afappsgrid"; if(cols == 2) { spanclass = "two-col-span nsappsgrid"; } if(cols == 1) { spanclass = "appLauncherVerticalAppsContainer"; } if(cols == 'fill') { spanclass = "appLauncherAppsContainer"; } function showHide(buttonKey) { if (buttonKey === '') { return 'hidden'; } } var buttonStyle = 'button'; const appLaunchButtons = new Map(); for(let i=1; i<=max; i++) { var appid = config["app_launch_"+i] || ''; appLaunchButtons.set("confBtn"+i, appid); } // Properly apply default launcher buttons var displayedRemote = config.defaultRemoteStyle_override || getDeviceAttribute("defaultRemoteStyle") || config.device_family; if(['AF1', 'AF2', 'AF3', 'AF4', 'AF5', 'AF6', 'AL1', 'amazon-fire'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'prime-video'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'netflix'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'disney-plus'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'hulu'); } else if(['AR1', 'AR2', 'AR3', 'apple-tv'].includes(displayedRemote)) { buttonStyle = 'button-round'; } else if(['CC1', 'CC2', 'CC3', 'chromecast'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'youtube'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'netflix'); buttonStyle = 'button-round'; } else if(['NS1', 'NS2', 'nvidia-shield'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); } else if(['XM1', 'XM2', 'xiaomi'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'prime-video'); } else if(['RSR', 'RVR'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'disney-plus'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'apple-tv'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'paramount-plus'); } else if(['RTR'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'disney-plus'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'hulu'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'roku-channel'); } else if(['RHR'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'hulu'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'roku-channel'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'disney-plus'); } else if(['RVRP', 'RWR', 'roku'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'netflix'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'disney-plus'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'apple-tv'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'hulu'); } else if(['AFJTV', 'AFXF2'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'prime-video'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'netflix'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'disney-plus'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'freeview-play'); } else if(['ON1', 'ON2'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'youtube'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'netflix'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'disney-plus'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'paramount-plus'); } else if(['HO1', 'HO2', 'HO3', 'HO4'].includes(displayedRemote)) { appLaunchButtons.set("confBtn1", config.app_launch_1 || 'youtube'); appLaunchButtons.set("confBtn2", config.app_launch_2 || 'netflix'); appLaunchButtons.set("confBtn3", config.app_launch_3 || 'prime-video'); appLaunchButtons.set("confBtn4", config.app_launch_4 || 'rocket-launcher-btn'); } // Return button HTML if(['CC1', 'CC2', 'CC3', 'AR1', 'AR2', 'AR3'].includes(config.defaultRemoteStyle_override) || (['apple-tv', 'chromecast'].includes(config.device_family) && !(config.defaultRemoteStyle_override))) { return html ` ${ Array.from(appLaunchButtons.keys()).map(key => { var val = appLaunchButtons.get(key); if(val) { return html ``; } })} `; } else { return html `
${ Array.from(appLaunchButtons.keys()).map(key => { var val = appLaunchButtons.get(key); return html ``; })}
`; } } // Draw optional device name function drawDeviceName(e, config, section){ if(!config.visible_name_text) { return }; if(config.name_position=='bottom' && section=='bottom') { return html`
${config.visible_name_text}
`; } else if(config.name_position=='top' && section=='top') { return html`
${config.visible_name_text}
`; } return; } // Draw optional Firemote Version number function drawFiremoteVersionNumber(e, config){ if(config.show_version_number === true) { return html `
${HAFiremoteVersion}
`; } } //Draw Optional MediaControl buttons on CC function drawMediaControlButtons(e, config) { if (config.show_media_controls==true) { return html` `; } return; } // Reused SVG Logos function renderfiretvlogo() { return html` `; } function renderAmazonArrowlogo() { return html` `; } function renderAmazonNameWithArrowLogo() { return html``; } function renderMiLogo() { return html``; } function renderXiaomiLogo() { return html``; } // Render Amazon Fire Remote Style AF1 // TODO: AF1 does not seem to scale properly if ( getDeviceAttribute('defaultRemoteStyle') == 'AF1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawDeviceName(this, this._config, 'bottom')} ${renderAmazonNameWithArrowLogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AF2 if ( getDeviceAttribute('defaultRemoteStyle') == 'AF2' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawDeviceName(this, this._config, 'bottom')} ${renderAmazonArrowlogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AF3 if ( getDeviceAttribute('defaultRemoteStyle') == 'AF3' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawDeviceName(this, this._config, 'bottom')} ${renderAmazonArrowlogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AF4 if ( getDeviceAttribute('defaultRemoteStyle') == 'AF4' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["AF4"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderfiretvlogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AF5 if ( getDeviceAttribute('defaultRemoteStyle') == 'AF5' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["AF5"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderfiretvlogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AF6 if ( getDeviceAttribute('defaultRemoteStyle') == 'AF6' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["AF6"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderfiretvlogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Amazon Fire Remote Style AFJTV or AFXF2 function renderBrandLogo(style) { if (style == 'AFJTV') { return html` `; } if (style == 'AFXF2') { return html` `; } } if ( getDeviceAttribute('defaultRemoteStyle') == 'AFJTV' || getDeviceAttribute('defaultRemoteStyle') == 'AFXF2') { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["AFJTV"])}
${renderBrandLogo(getDeviceAttribute('defaultRemoteStyle'))} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } function renderHomaticsExtras(remote, style){ if (style == 'HO1' || style == 'HO2') { return }; return html`
`; } // Render Homatics Remote Style HO1, HO2, HO3, HO4 if ( ['HO1', 'HO2', 'HO3', 'HO4'].includes(getDeviceAttribute('defaultRemoteStyle'))) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${renderHomaticsExtras(this, getDeviceAttribute('defaultRemoteStyle'))}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["ON1"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render NVIDIA Shield Remote Style NS1 if ( getDeviceAttribute('defaultRemoteStyle') == 'NS1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render NVIDIA Shield Remote Style NS2 if ( getDeviceAttribute('defaultRemoteStyle') == 'NS2' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 2, appButtonMax["NS2"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render onn. remote style 1 if ( getDeviceAttribute('defaultRemoteStyle') == 'ON1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["ON1"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render onn. remote style 2 if ( getDeviceAttribute('defaultRemoteStyle') == 'ON2' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["ON2"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } function renderRokuPowerRow(remote, style){ if(style=='RVR') { return html`
`; } else { return html`
`; } } function renderRokuDPad(remote) { return html`
`; } function renderRokuBackHomeRow(remote) { return html`
`; } function renderRokuReplayOptionsRow(remote, style) { var middleButton = html ``; if (style=='RHR'|| style=='RWR') { var middleButton = html ``; } if (style=='RSR' || style=='RTR') { middleButton = '' }; return html`
${middleButton}
`; } function renderRokuScrubRow(remote) { return html`
`; } function renderRokuTag() { return html`
`; } // Render Roku Hisense Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'RHR' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuPowerRow(this, 'RHR')} ${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this, "RHR")} ${renderRokuScrubRow(this)} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["RHR"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Roku Simple Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'RSR' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this, 'RSR')} ${renderRokuScrubRow(this)} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["RSR"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderRokuTag()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Roku TCL Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'RTR' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuPowerRow(this, 'RTR')} ${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this, "RTR")} ${renderRokuScrubRow(this)} ${drawAppLaunchButtons(this, this._config, 1, appButtonMax["RTR"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Roku Westinghouse Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'RWR' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuPowerRow(this, 'RWR')} ${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this, "RWR")} ${renderRokuScrubRow(this)} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["RWR"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Roku Voice Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'RVR' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuPowerRow(this, 'RVR')} ${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this)} ${renderRokuScrubRow(this)} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["RVR"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderRokuTag()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Roku Voice Remote Pro if ( getDeviceAttribute('defaultRemoteStyle') == 'RVRP' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${renderRokuPowerRow(this)}
${renderRokuBackHomeRow(this)} ${renderRokuDPad(this)} ${renderRokuReplayOptionsRow(this)} ${renderRokuScrubRow(this)}
${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["RVRP"])} ${drawDeviceName(this, this._config, 'bottom')} ${renderRokuTag()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Xiaomi Remote 1 if ( getDeviceAttribute('defaultRemoteStyle') == 'XM1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["XM1"])}
${drawDeviceName(this, this._config, 'bottom')} ${renderXiaomiLogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Xiaomi Mi Remote if ( getDeviceAttribute('defaultRemoteStyle') == 'XM2' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 3, appButtonMax["XM2"])}
${drawDeviceName(this, this._config, 'bottom')} ${renderMiLogo()} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Chromecast 1, 2, or 3 if ( ['CC1', 'CC2', 'CC3'].includes(getDeviceAttribute('defaultRemoteStyle'))) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawMediaControlButtons(this, this._config)} ${drawAppLaunchButtons(this, this._config, 2, appButtonMax[getDeviceAttribute('defaultRemoteStyle')])}
${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Apple TV Remote - Style 1 if ( getDeviceAttribute('defaultRemoteStyle') == 'AR1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 2, appButtonMax["AR1"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Apple TV Remote - Style 2 if ( getDeviceAttribute('defaultRemoteStyle') == 'AR2' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 2, appButtonMax["AR2"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render Apple TV Remote - Style 3 if ( getDeviceAttribute('defaultRemoteStyle') == 'AR3' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')}
${drawAppLaunchButtons(this, this._config, 2, appButtonMax["AR3"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render App Launcher 1 if ( getDeviceAttribute('defaultRemoteStyle') == 'AL1' ) { return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["AL1"])} ${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } // Render App Launcher 2 if ( getDeviceAttribute('defaultRemoteStyle') == 'AL2' ) { var lpb4icon = 'mdi:rewind'; var lpb6icon = 'mdi:fast-forward'; var rpb2icon = 'mdi:restart'; var rpb2id = 'reboot-button'; var hiddenclass = ''; if(this._config.device_family == 'apple-tv') { var lpb4icon = 'mdi:skip-backward'; var lpb6icon = 'mdi:skip-forward'; var rpb2icon = 'mdi:magnify'; var rpb2id = 'search-button'; var hiddenclass = 'hidden'; } // conditionally hide the app launcher section if no apps are selected var configuredAppLaunchButtons = 0; for(let i=1; i<=appmap.size; i++) { if(this._config["app_launch_"+i] && this._config["app_launch_"+i] != '') { configuredAppLaunchButtons++; } } var noAppsClass = ''; if (configuredAppLaunchButtons==0){ noAppsClass = 'noApps'; } // conditionally hide the button frame sections and their backgrounds var rowClass = "row"; if(this._config.hide_button_group_frame == true) { rowClass="row noframes"; } // allow the dpad style to change for AL2 remotes function getDpad(caller, style='amazon-fire') { var dpadClass = ''; switch(style) { case 'apple-tv-black': dpadClass = 'AR3 '; break; case 'apple-tv-silver': dpadClass = 'AR1 '; break; case 'chromecast': dpadClass = 'CC '; break; case 'xiaomi': dpadClass = 'XM '; break; case 'minimal': dpadClass = "minimal "; break; } if(style=='minimal'){ return html`
`; } else { return html`
`; } } return html` ${cssVars}
${drawDeviceName(this, this._config, 'top')} ${drawAppLaunchButtons(this, this._config, 'fill', appButtonMax["AL2"])}
${getDpad(this, this._config.dpad_style)}


${drawDeviceName(this, this._config, 'bottom')} ${drawFiremoteVersionNumber(this, this._config)}
`; } } // Firemote Button Click or Click + Hold Handler buttonDown(clicked) { // Ignore right clicks if (clicked.which === 3) { return; } // Inspect user prefs & setup click timer const _config = this._config; const _hass = this.hass; const deviceType = this._config.device_type; const deviceFamily = this._config.device_family; const entity = this._config.entity; const compatibility_mode = this._config.compatibility_mode || 'default'; const overrides = this._config.button_overrides; const atvRemoteEntity = this._config.android_tv_remote_entity; const rokuRemoteEntity = this._config.roku_remote_entity; const activityLight = this.renderRoot.querySelector('.activityLight'); const start = Date.now(); const holdTime = 500; const buttonID = clicked.target.id; var pressedTarget = clicked.target; var timer = null; // Rebuild AppMap - allow hdmi inputs where appropriate & add configured custom launchers from YAML refreshAppMap(this._config, 4, 1, true); // Function for buttons that need overrides because HA integration support is lacking function unsupportedButton(){ alert('"'+buttonID+"\"\n\nUse a button override to program this button\nhttps://github.com/PRProd/HA-Firemote/#button-overrides"); return; } // Check for user set Associated Android TV Remote Integration entity var hasATVAssociation = true; if(atvRemoteEntity == '' || typeof atvRemoteEntity == 'undefined' || deviceFamily == 'amazon-fire' ) { hasATVAssociation = false; } // Choose event listener path for client android device var eventListenerBinPath = ''; if(compatibility_mode == 'default' || compatibility_mode == 'strong' || compatibility_mode == '') { eventListenerBinPath = deviceAttributeQuery("defaultEventListenerBinPath", this._config); } else { var eventListenerBinPath = '/dev/input/'+compatibility_mode; } // Function to handle translations from English to the user's language const ha_language = this.hass.config.language; function translateToUsrLang(englishString) { var translatedString = englishString; if (typeof translationmap.get(ha_language) !== 'undefined'){ if (typeof translationmap.get(ha_language)[sourceName] !== 'undefined'){ translatedString = translationmap.get(ha_language)[englishString]; } } return translatedString; } // Function: Assemble arguments for service calls function getCustomServiceArgs(def) { if((typeof def.action !== 'undefined' || typeof def.service !== 'undefined') && typeof def.target !== 'undefined') { var svcarray; if(typeof def.action !== 'undefined') { svcarray = def.action.split("."); } else { svcarray = def.service.split("."); } var data = Object; if(typeof def.data !== 'undefined') { var extraData = JSON.parse(JSON.stringify(def.data)); var target = JSON.parse(JSON.stringify(def.target)); data = Object.assign(target, extraData); } else { data = Object.assign(def.target); } return new Array(svcarray[0], svcarray[1], data); } }; // Check for and handle button overrides before proceeding // No HOLD actions for button overrides are supported if(typeof overrides !== 'undefined' && overrides !== null) { if(typeof overrides[buttonID] !== 'undefined') { const overrideDef = overrides[buttonID]; if(overrideDef !== null) { if(typeof overrideDef.script !== 'undefined') { // handle overrides via external script try{ _hass.callService("script", overrideDef.script, overrideDef.data) } catch { return; } fireEvent(this, 'haptic', 'light'); // haptic feedback on success return; } else if((typeof overrideDef.service !== 'undefined' || typeof overrideDef.action !== 'undefined') && typeof overrideDef.target !== 'undefined') { // handle overrides via yaml instructions var ServiceDetails = getCustomServiceArgs(overrideDef); try{ _hass.callService(ServiceDetails[0], ServiceDetails[1], ServiceDetails[2]) } catch { return; } fireEvent(this, 'haptic', 'light'); // haptic feedback on success return; } } } } // Handle custom launcher button clicks // No HOLD actions for custom launchers are supported var customLauncherIDPfx = new RegExp('customlauncher ') if(customLauncherIDPfx.test(buttonID)){ var clickedButtonID = buttonID; const customLauncherKey = clickedButtonID.substr(0, clickedButtonID.indexOf("-button")); if(appmap.has(customLauncherKey)) { if(appmap.get(customLauncherKey).script) { try{ _hass.callService("script", appmap.get(customLauncherKey).script, appmap.get(customLauncherKey).data) } catch { return; } fireEvent(this, 'haptic', 'light'); return; } else { var launcher = appmap.get(customLauncherKey); if(typeof launcher.friendlyName !== 'undefined') { _config.custom_launchers.forEach(element => { var name = launcher.friendlyName.substr(8); if (name == element.friendly_name) { var ServiceDetails = getCustomServiceArgs(element); try{ _hass.callService(ServiceDetails[0], ServiceDetails[1], ServiceDetails[2]) } catch { return; } fireEvent(this, 'haptic', 'light'); } }); return; } } } } // Handle Built-in App launcher buttons (existing in JSON map) // No HOLD actions for app launchers are supported const appkey = buttonID.substr(0, buttonID.indexOf("-button")); if(appmap.has(appkey)) { var familySpecificAppData = appmap.get(appkey)[deviceFamily]; if(familySpecificAppData && (familySpecificAppData.adbLaunchCommand || familySpecificAppData.appName || familySpecificAppData.remoteCommand)) { var adbcommand = familySpecificAppData.adbLaunchCommand; var sourceName = familySpecificAppData.appName; var remoteCommand = familySpecificAppData.remoteCommand; } else { var adbcommand = appmap.get(appkey).adbLaunchCommand; var sourceName = appmap.get(appkey).appName; var remoteCommand = appmap.get(appkey).remoteCommand } sourceName = translateToUsrLang(sourceName); fireEvent(this, 'haptic', 'light'); if (typeof remoteCommand != 'undefined' && ['apple-tv', 'roku'].includes(deviceFamily)) { var data = JSON.parse(remoteCommand); switch (deviceFamily) { case 'apple-tv': data['entity_id'] = _config.apple_tv_remote_entity; break; case 'roku': data['entity_id'] = rokuRemoteEntity; break; } _hass.callService("remote", "send_command", data); return; } if (typeof adbcommand == 'undefined') { _hass.callService("media_player", "select_source", { entity_id: _config.entity, source: sourceName}); return; } else { if(adbcommand == 'adb shell reboot') { if(confirm('Are you sure you want to reboot '+_hass.states[_config.entity].attributes.friendly_name) == false) { return; } } _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: adbcommand }); return; } return; } // Catch-all for none/other configurations where a button hasn't been defined in YAML config if(_config.entity === 'none') { unsupportedButton(); return; } // detect difference between click or hold actions // then carry out the appropriate actions const userAction = getActionType(this); async function getActionType(that) { let type = new Promise((resolve, reject) => { pressedTarget.addEventListener("mouseup", release, true); pressedTarget.addEventListener("pointerup", release, true); pressedTarget.addEventListener("mouseout", mouseOutHandler, true); pressedTarget.addEventListener('touchmove', handleTouchMove, {passive: true}); timer = setTimeout(function(){ if(start){ // it was a hold pressedTarget.removeEventListener("mouseup", release, true); pressedTarget.removeEventListener("pointerup", release, true); pressedTarget.removeEventListener("mouseout", mouseOutHandler, true); pressedTarget.removeEventListener('touchmove', handleTouchMove, {passive: true}); resolve("hold"); } }, holdTime); // it was a click function release(){ clearTimeout(timer); let start = null; pressedTarget.removeEventListener("mouseup", release, true); pressedTarget.removeEventListener("pointerup", release, true); pressedTarget.removeEventListener("mouseout", mouseOutHandler, true); pressedTarget.removeEventListener('touchmove', handleTouchMove, {passive: true}); resolve("click"); } // a non-event is cancelled here function mouseOutHandler() { clearTimeout(timer); let start = null; pressedTarget.removeEventListener("mouseup", release, true); pressedTarget.removeEventListener("pointerup", release, true); pressedTarget.removeEventListener("mouseout", mouseOutHandler, true); pressedTarget.removeEventListener('touchmove', handleTouchMove, {passive: true}); resolve(null); } function handleTouchMove() { clearTimeout(timer); let start = null; pressedTarget.removeEventListener("mouseup", release, true); pressedTarget.removeEventListener("pointerup", release, true); pressedTarget.removeEventListener("mouseout", mouseOutHandler, true); pressedTarget.removeEventListener('touchmove', handleTouchMove, {passive: true}); resolve(null); } }); // After determining the button interaction type, // send the appropriate commands let actionType = await type; if (actionType === null) {return;} if(actionType == 'click' || actionType == 'hold') { // Flash activity light if it exists if (activityLight) { activityLight.style.opacity = '1'; setTimeout(function() {activityLight.style.opacity = '0'}, 100); } // provide haptic feedback for button press fireEvent(that, 'haptic', 'light') // Power Button Click if(buttonID == 'power-button' && actionType == 'click') { const state = _hass.states[entity]; const stateStr = state ? state.state : 'off'; if(['apple-tv', 'roku'].includes(deviceFamily)) { if(stateStr != 'off' && stateStr != 'unavailable' && stateStr != 'standby') { _hass.callService("media_player", "turn_off", { entity_id: entity}); } else { _hass.callService("media_player", "turn_on", { entity_id: entity}); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_POWER' }); } else if (compatibility_mode == 'strong' && eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'POWER' }); } else if(compatibility_mode == 'strong') { _hass.callService("media_player", "toggle", { entity_id: entity}); } else if(deviceType == 'fire_stick_4k' || deviceType == 'fire_tv_stick_4k_max' || deviceType == 'fire_tv_3rd_gen' || deviceType =='fire_stick_second_gen' || deviceType == 'fire_tv_stick_4k_second_gen' ) { if(stateStr != 'off' && stateStr != 'unavailable') { _hass.callService("media_player", "turn_off", { entity_id: entity}); } else { _hass.callService("media_player", "turn_on", { entity_id: entity}); } } else if (deviceType == 'fire_tv_cube_third_gen') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 116 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 116 0 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent /dev/input/event2 1 9 1 && sendevent /dev/input/event2 0 0 0 && sendevent /dev/input/event2 1 9 0 && sendevent /dev/input/event2 0 0 0' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'POWER' }); } return; } // Power Button Hold if(buttonID == 'power-button' && actionType == 'hold') { if(['amazon-fire', 'apple-tv', 'chromecast', 'nvidia-shield', 'onn', 'roku'].includes(deviceFamily)) { return; } if(deviceFamily == 'xiaomi'){ if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_POWER', num_repeats: 1, delay_secs: 0, hold_secs: 0.75 }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent --longpress POWER'}); } return; } } // Account Button (Google TV) Click if(buttonID == 'profile-button' && actionType == 'click') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent KEYCODE_PROFILE_SWITCH' }); return; }; // Account Button (Google TV) Hold if(buttonID == 'profile-button' && actionType == 'hold') { // no special behaviors found for this yet return; } // Magic Button / Star Button (Google TV / onn pro) Click if(buttonID == 'magic-star-button' && actionType == 'click') { if (deviceType == 'onn-streaming-device-4k-pro') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent 313' }); return; } else { unsupportedButton(); return; } }; // Magic Button / Star Button (Google TV / onn pro) Hold if(buttonID == 'magic-star-button' && actionType == 'hold') { if (deviceType == 'onn-streaming-device-4k-pro') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent --longpress 313' }); return; } else { unsupportedButton(); return; } }; // Keyboard button click if(buttonID == 'keyboard-button' && actionType == 'click') { if(['apple-tv'].includes(deviceFamily)) { unsupportedButton(); return; } var text = prompt("Enter text to send"); if (text && text != '') { if(['roku'].includes(deviceFamily)) { _hass.callService("remote", "send_command", { entity_id: rokuRemoteEntity, command: 'Lit_'+text, num_repeats: 1, delay_secs: 0, hold_secs: 0}); } else { var escapedText = text.replace(/"/g, "\\\""); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'input text "'+escapedText+'"' }); } } return; }; // Keyboard button hold if(buttonID == 'keyboard-button' && actionType == 'hold') { // no special behaviors dreamed up for this yet return; } // Up Button click if(buttonID == 'up-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_UP' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'UP' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 103 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 103 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Up Button hold if(buttonID == 'up-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // needs to be at least 1sec hold time for Apple TV _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'up', num_repeats: 1, delay_secs: 0, hold_secs: 1}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'up', num_repeats: 1, delay_secs: 0, hold_secs: 1}); }, 1025); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 1/4 sec repeat is an ok substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_UP', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_UP', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); }, 525); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'UP' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'UP' }); }, 250); function rls() { clearInterval(rpt); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 103 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 103 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Left Button Click if(buttonID == 'left-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'left', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_LEFT' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'LEFT' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 105 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 105 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Left Button Hold if(buttonID == 'left-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // needs to be at least 1sec hold time for Apple TV _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'left', num_repeats: 1, delay_secs: 0, hold_secs: 1}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'left', num_repeats: 1, delay_secs: 0, hold_secs: 1}); }, 1025); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 1/4 sec repeat is an ok substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'left', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'left', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_LEFT', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_LEFT', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); }, 525); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'LEFT' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'LEFT' }); }, 250); function rls() { clearInterval(rpt); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 105 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 105 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Right Button Click if(buttonID == 'right-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'right', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_RIGHT' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'RIGHT' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 106 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 106 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Right Button Hold if(buttonID == 'right-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // needs to be at least 1sec hold time for Apple TV _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'right', num_repeats: 1, delay_secs: 0, hold_secs: 1}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'right', num_repeats: 1, delay_secs: 0, hold_secs: 1}); }, 1025); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 1/4 sec repeat is an ok substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'right', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'right', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_RIGHT', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_RIGHT', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); }, 525); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'RIGHT' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'RIGHT' }); }, 250); function rls() { clearInterval(rpt); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 106 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 106 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Down Button Click if(buttonID == 'down-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_DOWN' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'DOWN' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 108 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 108 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Down Button Hold if(buttonID == 'down-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // needs to be at least 1sec hold time for Apple TV _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'down', num_repeats: 1, delay_secs: 0, hold_secs: 1}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'down', num_repeats: 1, delay_secs: 0, hold_secs: 1}); }, 1025); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 1/4 sec repeat is an ok substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_DOWN', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_DOWN', num_repeats: 1, delay_secs: 0, hold_secs: 0.5}); }, 525); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'DOWN' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'DOWN' }); }, 250); function rls() { clearInterval(rpt); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 108 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 108 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Center Button Click if(buttonID == 'center-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'select', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_CENTER' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'CENTER' }); } else { if(deviceType == 'fire_tv_4_series' || deviceType == 'fire_tv_toshiba_v35' || deviceType == 'fire_tv_jvc-4k-2021') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 28 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 28 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } else if(deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 353 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 353 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 96 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 96 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } } return; } // Center Button Hold if(buttonID == 'center-button' && actionType == 'hold') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); // This strategy does not work with Roku - https://github.com/home-assistant/core/issues/123999 _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'select', num_repeats: 1, delay_secs: 0, hold_secs: 1}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_DPAD_CENTER', num_repeats: 1, delay_secs: 0, hold_secs: 0.75 }); return; } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { // This adb shell input keyevent --longpress 23 strategy does not seem to work with the NVIDIA Shield, onn. or Chromecast through ADB // need to rapidfire the center button like we do the other dpad buttons _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent 23' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'adb shell input keyevent 23' }); }, 250); function rls() { clearInterval(rpt); } } else { if(deviceType == 'fire_tv_4_series' || deviceType == 'fire_tv_toshiba_v35' || deviceType == 'fire_tv_jvc-4k-2021') { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 28 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 28 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } else if(deviceType == 'mi-box-s') { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 353 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 353 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 96 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 96 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } } return; } // Replay Button Click if(buttonID == 'replay-button' && actionType == 'click') { if(['roku'].includes(deviceFamily)) { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'replay', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } else { unsupportedButton(); return; } } // Replay Button Hold if(buttonID == 'replay-button' && actionType == 'hold') { if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 1/4 sec repeat is an ok substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'replay', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'replay', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } return; } // Options Button Click if(buttonID == 'options-button' && actionType == 'click') { if(['roku'].includes(deviceFamily)) { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'info', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } } // Options Button Hold if(buttonID == 'options-button' && actionType == 'hold') { // no special behaviors found for this yet return; } // Apps Button Click if(buttonID == 'apps-button' && actionType == 'click') { if (deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start -n com.google.android.tvlauncher/.appsview.AppsViewActivity' }); } else if (deviceFamily == 'nvidia-shield') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_APP_SWITCH' }); } else if (deviceFamily == 'amazon-fire') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'am start -n com.amazon.venezia/com.amazon.venezia.grid.AppsGridLauncherActivity' }); } else if (deviceFamily == 'onn') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_ALL_APPS' }); } else if (deviceFamily == 'roku') { unsupportedButton(); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'RECENTS' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 757 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 757 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Apps Button Hold if(buttonID == 'apps-button' && actionType == 'hold') { // no special behaviors found for this yet return; } // Back Button Click if(buttonID == 'back-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'menu', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'back', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_BACK' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'BACK' }); } return; } // Back Button Hold if(buttonID == 'back-button' && actionType == 'hold') { // no special behaviors found for this with Chromecast, FireTV, onn., NVIDIA Shield, Roku, or Xiaomi mi if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'home', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } return; } // Home Button Click if(buttonID == 'home-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'top_menu', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'home', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_HOME' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'HOME' }); } return; } // Home Button Hold if(buttonID == 'home-button' && actionType == 'hold') { // no special behavior noticed for Roku if(deviceFamily == 'amazon-fire') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent SETTINGS' }); } else if(['chromecast', 'onn'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 83' }); } else if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'home', num_repeats: 1, delay_secs: 0, hold_secs: 1}); } else if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_HOME', num_repeats: 1, delay_secs: 0, hold_secs: 0.75}); } else if(['nvidia-shield', 'xiaomi'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_ALL_APPS' }); } return; } // Hamburger Button Click if(buttonID == 'hamburger-button' && actionType == 'click') { if(['apple-tv', 'roku', 'onn'].includes(deviceFamily)) { unsupportedButton(); return; } if(deviceType == 'shield-tv-pro-2019' || deviceType == 'shield-tv-2019') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'am start -a android.settings.SETTINGS' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'MENU' }); } else if(['xiaomi'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start -n com.android.tv.settings/com.android.tv.settings.MainSettings' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 139 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 139 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Hamburger Button Hold if(buttonID == 'hamburger-button' && actionType == 'hold') { // no special behaviors found for this with FireTV, or NVIDIA Shield // the hamburger button for Xiaomi isn't actually real return; } // Rewind Button Click if(buttonID == 'rewind-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'skip_backward', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'reverse', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_MEDIA_REWIND' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined' || deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'REWIND' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 168 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 168 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Rewind Button Hold if(buttonID == 'rewind-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // n/a for Apple TV (assuming rewind translates to skip_backward) return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 0.15 sec repeat is an ok-ish substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'reverse', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'reverse', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 150); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_MEDIA_REWIND', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_MEDIA_REWIND', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 100); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'REWIND' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'REWIND' }); }, 250); function rls() { clearInterval(rpt); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 168 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 168 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Play/Pause Button Click if(buttonID == 'playpause-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { var playpausecommand = 'pause'; if(_hass.states[_config.entity].state=='paused'){ playpausecommand = 'play'; }; _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: playpausecommand, num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'play', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_MEDIA_PLAY_PAUSE' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined' || deviceType == 'mi-box-s') { _hass.callService("media_player", "media_play_pause", { entity_id: _config.entity}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 164 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 164 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Play/Pause Button Hold if(buttonID == 'playpause-button' && actionType == 'hold') { // Button does not exist for apple tv, chromecast, onn., or xiaomi - so no hold behaviors are emulated // No special behaviors noticed for this with FireTV, NVIDIA Shield, or Roku return; } // Fast Forward Button Click if(buttonID == 'fastforward-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'skip_forward', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'forward', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_MEDIA_FAST_FORWARD' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined' || deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'FAST_FORWARD' }); } else { if(deviceType == 'fire_tv_4_series' || deviceType == 'fire_tv_toshiba_v35' || deviceType == 'fire_tv_jvc-4k-2021') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 159 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 159 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 208 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 208 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } } return; } // Fast Forward Button Hold if(buttonID == 'fastforward-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { // n/a for Apple TV (assuming fast forward translates to skip_forward) return; } if(['roku'].includes(deviceFamily)) { // hold does not work with Roku - https://github.com/home-assistant/core/issues/123999 - using a 0.15 sec repeat is an ok-ish substitute for now _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'forward', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'forward', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 150); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_MEDIA_FAST_FORWARD', num_repeats: 1, delay_secs: 0, hold_secs: 0}); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: atvRemoteEntity, command: 'KEYCODE_MEDIA_FAST_FORWARD', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 100); function rls() { clearInterval(rpt); } } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'FAST_FORWARD' }); clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'FAST_FORWARD' }); }, 250); function rls() { clearInterval(rpt); } } else { if(deviceType == 'fire_tv_4_series' || deviceType == 'fire_tv_toshiba_v35' || deviceType == 'fire_tv_jvc-4k-2021') { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 159 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 159 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 208 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 208 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } } // Volume Up Button Click if(buttonID == 'volume-up-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'volume_up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_VOLUME_UP' }); } else if(deviceFamily == 'nvidia-shield') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell cmd media_session volume --show --adj raise' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'VOLUME_UP' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 115 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 115 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Volume Up Button Hold if(buttonID == 'volume-up-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'volume_up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'volume_up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_VOLUME_UP' }); }, 250); function rls() { clearInterval(rpt); } return; } else if(deviceFamily == 'nvidia-shield') { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell cmd media_session volume --show --adj raise' }); }, 250); function rls() { clearInterval(rpt); } return; } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'VOLUME_UP' }); }, 250); function rls() { clearInterval(rpt); } return; } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 115 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 115 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Volume Down Button Click if(buttonID == 'volume-down-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { var t = deviceFamily+"_remote_entity"; var confname = t.replace(/\-/, "_"); _hass.callService("remote", "send_command", { entity_id: _config[confname], command: 'volume_down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_VOLUME_DOWN' }); } else if(deviceFamily == 'nvidia-shield') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell cmd media_session volume --show --adj lower' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'VOLUME_DOWN' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 114 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 114 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Volume Down Button Hold if(buttonID == 'volume-down-button' && actionType == 'hold') { if(['apple-tv'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.apple_tv_remote_entity, command: 'volume_down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(['roku'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'volume_down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } if(hasATVAssociation) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_VOLUME_DOWN' }); }, 250); function rls() { clearInterval(rpt); } return; } else if(deviceFamily == 'nvidia-shield') { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell cmd media_session volume --show --adj lower' }); }, 250); function rls() { clearInterval(rpt); } return; } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'VOLUME_DOWN' }); }, 250); function rls() { clearInterval(rpt); } return; } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 114 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 114 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } // Mute Button Click if(buttonID == 'mute-button' && actionType == 'click') { if(deviceFamily == 'apple-tv') { unsupportedButton(); } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'volume_mute', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if(hasATVAssociation) { _hass.callService("remote", "send_command", { entity_id: _config.android_tv_remote_entity, command: 'KEYCODE_VOLUME_MUTE' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'MUTE' }); } else if (deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 164'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 113 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 113 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Mute Button Hold if(buttonID == 'mute-button' && actionType == 'hold') { // Button does not exist for chromecast, NVIDIA Shield, or xiaomi - so no hold behaviors are emulated // No special behaviors noticed for this with Apple TV, FireTV, onn., or Roku return; } // Channel Up Button Click if(buttonID == 'channel-up-button' && actionType == 'click') { if(['apple-tv', 'chromecast', 'nvidia-shield', 'xiaomi'].includes(deviceFamily)) { unsupportedButton(); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'channel_up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if (['homatics'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_UP'}); return; } if (['fire_tv_stick_4k_second_gen', 'fire_tv_stick_4k_max_second_gen', 'onn-streaming-device-4k-pro', 'onn-4k-streaming-box', 'onn-full-hd-streaming-stick'].includes(deviceType)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_UP'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 402 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 402 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Channel Up Button Hold if(buttonID == 'channel-up-button' && actionType == 'hold') { // No special behaviors noticed for this with onn. // Button does not exist for Apple TV, Chromecast, Homatics, NVIDIA Shield, or Xiaomi - so no hold behaviors are emulated if(['onn', 'apple-tv', 'chromecast', 'homatics', 'nvidia-shield', 'xiaomi'].includes(deviceFamily)) { return; } if(['roku'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'channel_up', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } else { if (['fire_tv_stick_4k_second_gen', 'fire_tv_stick_4k_max_second_gen'].includes(deviceType)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_UP'}); }, 250); function rls() { clearInterval(rpt); } return; } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 402 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 402 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } } // Channel Down Button Click if(buttonID == 'channel-down-button' && actionType == 'click') { if(['apple-tv', 'chromecast', 'nvidia-shield', 'xiaomi'].includes(deviceFamily)) { unsupportedButton(); return; } if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'channel_down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } if (['homatics'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_DOWN'}); return; } if (['fire_tv_stick_4k_second_gen', 'fire_tv_stick_4k_max_second_gen', 'onn-streaming-device-4k-pro', 'onn-4k-streaming-box', 'onn-full-hd-streaming-stick'].includes(deviceType)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_DOWN'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 403 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 403 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Channel Down Button Hold if(buttonID == 'channel-down-button' && actionType == 'hold') { // No special behaviors noticed for this with onn. // Button does not exist for Apple TV, Chromecast, NVIDIA Shield, or Xiaomi - so no hold behaviors are emulated if(['onn', 'apple-tv', 'chromecast', 'homatics', 'nvidia-shield', 'xiaomi'].includes(deviceFamily)) { return; } if(['roku'].includes(deviceFamily)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'channel_down', num_repeats: 1, delay_secs: 0, hold_secs: 0}); }, 250); function rls() { clearInterval(rpt); } return; } else { if (['fire_tv_stick_4k_second_gen', 'fire_tv_stick_4k_max_second_gen'].includes(deviceType)) { clicked.target.addEventListener("pointerup", rls, true); let rpt = setInterval( function() { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_CHANNEL_DOWN'}); }, 250); function rls() { clearInterval(rpt); } return; } else { clicked.target.addEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 403 1 && sendevent '+eventListenerBinPath+' 0 0 0' }); function rls() { clicked.target.removeEventListener("pointerup", rls, true); _hass.callService("androidtv", "adb_command", { entity_id: entity, command: 'sendevent '+eventListenerBinPath+' 1 403 0 && sendevent '+eventListenerBinPath+' 0 0 0 '}); } } return; } } // TV Button Click if(buttonID == 'tv-button' && actionType == 'click') { if(['apple-tv', 'roku', 'nvidia-shield'].includes(deviceFamily)) { unsupportedButton(); return; } if (deviceType == 'fire_tv_cube_third_gen') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 297'}); } else if (deviceType == 'fire_tv_stick_4k_second_gen' || deviceType == 'fire_tv_stick_4k_max_second_gen') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 300'}); } else if (deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start -n com.google.android.tv/com.android.tv.MainActivity' }); } else if (['onn', 'homatics'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_GUIDE'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 362 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 362 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // TV Button Button Hold if(buttonID == 'tv-button' && actionType == 'hold') { // no special behaviors found for this return; } // Settings Button Click if(buttonID == 'settings-button' && actionType == 'click') { if(['roku', 'nvidia-shield'].includes(deviceFamily)) { unsupportedButton(); return; } if (['onn'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 83' }); } else if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined' || deviceType == 'fire_tv_cube_third_gen') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'SETTINGS' }); } else if(deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start -n com.android.tv.settings/com.android.tv.settings.MainSettings' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 249 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 249 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Settings Button Button Hold if(buttonID == 'settings-button' && actionType == 'hold') { // no special behaviors found for this return; } // App Switch (recents) Button if(buttonID == 'app-switch-button' && actionType == 'click') { if(compatibility_mode == 'strong') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'RECENTS' }); } else if (deviceType == 'fire_tv_stick_4k_second_gen' || deviceType == 'fire_tv_stick_4k_max_second_gen') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 307'}); } else if(eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'RECENTS' }); } else if (deviceType == 'fire_tv_cube_third_gen') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 304'}); } else if (deviceType == 'mi-box-s') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_APP_SWITCH' }); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 757 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 757 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // App Switch (recents) Button Hold if(buttonID == 'app-switch-button' && actionType == 'hold') { // no special behaviors found for this return; } // Headset Button Click if(buttonID == 'headset-button' && actionType == 'click') { if (['amazon-fire'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent BUTTON_3'}); } else if (['onn'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_PAIRING'}); } else if (['chromecast', 'nvidia-shield', 'xiaomi'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start -n com.android.tv.settings/com.android.tv.settings.accessories.AddAccessoryActivity'}); } else { unsupportedButton(); return; } return; } // Headset Button Hold if(buttonID == 'headset-button' && actionType == 'hold') { // no special behaviors found for this return; } // Programmable 1 Button Click if(buttonID == 'programmable-one-button' && actionType == 'click') { if(['amazon-fire'].includes(deviceFamily)) { if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent BUTTON_1'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 638 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 638 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } else { unsupportedButton(); return; } } // Programmable 1 Button Hold if(buttonID == 'programmable-one-button' && actionType == 'hold') { if(['amazon-fire'].includes(deviceFamily)) { if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { //TODO: - (spent too much time here already) } else { //_hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 638 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sleep 1 && sendevent '+eventListenerBinPath+' 1 638 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); // TODO: sleep does not work as expected? (spent too much time here already) } return; } } // Programmable 2 Button Click if(buttonID == 'programmable-two-button' && actionType == 'click') { if(['amazon-fire'].includes(deviceFamily)) { if(compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent BUTTON_2'}); // this is wrong for fire_tv_4_series } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 4 4 787071 && sendevent '+eventListenerBinPath+' 1 639 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 4 4 787071 && sendevent '+eventListenerBinPath+' 1 639 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); // wrong w/event0 on ftv 4 } return; } else { unsupportedButton(); return; } } // Programmable 2 Button Hold if(buttonID == 'programmable-two-button' && actionType == 'hold') { // TODO - (spent too much time here already) return; } // Info Button Click if(buttonID == 'info-button' && actionType == 'click') { if(!(['homatics'].includes(deviceFamily))) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 165'}); } return; } // Info Button Click if(buttonID == 'info-button' && actionType == 'hold') { // no special behaviors known for this return; } // Bookmark Button Click if(buttonID == 'bookmark-button' && actionType == 'click') { if(!(['homatics'].includes(deviceFamily))) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_BOOKMARK'}); } return; } // Bookmark Button Click if(buttonID == 'bookmark-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 1 Button Click if(buttonID == 'num1-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 8'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 2 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 2 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 1 Button Hold if(buttonID == 'num1-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 2 Button Click if(buttonID == 'num2-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 9'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 3 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 3 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 2 Button Hold if(buttonID == 'num2-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 3 Button Click if(buttonID == 'num3-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 10'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 4 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 4 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 3 Button Hold if(buttonID == 'num3-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 4 Button Click if(buttonID == 'num4-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 11'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 5 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 5 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 4 Button Hold if(buttonID == 'num4-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 5 Button Click if(buttonID == 'num5-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 12'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 6 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 6 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 5 Button Hold if(buttonID == 'num5-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 6 Button Click if(buttonID == 'num6-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 13'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 7 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 7 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 6 Button Hold if(buttonID == 'num6-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 7 Button Click if(buttonID == 'num7-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 14'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 8 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 8 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 7 Button Hold if(buttonID == 'num7-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 8 Button Click if(buttonID == 'num8-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 15'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 9 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 9 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 8 Button Hold if(buttonID == 'num8-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 9 Button Click if(buttonID == 'num9-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 16'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 10 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 10 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 9 Button Hold if(buttonID == 'num9-button' && actionType == 'hold') { // no special behaviors known for this return; } // Numeric 0 Button Click if(buttonID == 'num0-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 7'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 11 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 11 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // Numeric 0 Button Hold if(buttonID == 'num0-button' && actionType == 'hold') { // no special behaviors known for this return; } // SUBT Button Click if(buttonID == 'subtitle-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else if (compatibility_mode == 'strong' || eventListenerBinPath == 'undefined') { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 175'}); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+eventListenerBinPath+' 1 469 1 && sendevent '+eventListenerBinPath+' 0 0 0 && sendevent '+eventListenerBinPath+' 1 469 0 && sendevent '+eventListenerBinPath+' 0 0 0' }); } return; } // SUBT Button Hold if(buttonID == 'subtitle-button' && actionType == 'hold') { // no special behaviors known for this return; } // LIVE Button Click if(buttonID == 'live-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { if(deviceType == 'fire_tv_jvc-4k-2021') { var tempbinpath = '/dev/input/event4' _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'sendevent '+tempbinpath+' 1 358 1 && sendevent '+tempbinpath+' 0 0 0 && sendevent '+tempbinpath+' 1 358 0 && sendevent '+tempbinpath+' 0 0 0' }); } else if (['fire_tv_4_series', 'fire_tv_toshiba_v35"'].includes(deviceType)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent KEYCODE_TV'}); } else { unsupportedButton(); } } return; } // LIVE Button Hold if(buttonID == 'live-button' && actionType == 'hold') { // no special behaviors known for this return; } // Red Button Click if(buttonID == 'red-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 183'}); } return; } // Red Button Hold if(buttonID == 'red-button' && actionType == 'hold') { // no special behaviors known for this return; } // Green Button Click if(buttonID == 'green-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 184'}); } return; } // Green Button Hold if(buttonID == 'green-button' && actionType == 'hold') { // no special behaviors known for this return; } // Yellow Button Click if(buttonID == 'yellow-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 185'}); } return; } // Yellow Button Hold if(buttonID == 'yellow-button' && actionType == 'hold') { // no special behaviors known for this return; } // Blue Button Click if(buttonID == 'blue-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 186'}); } return; } // Blue Button Hold if(buttonID == 'blue-button' && actionType == 'hold') { // no special behaviors known for this return; } // Voice Button Click if(buttonID == 'voice-button' && actionType == 'click') { unsupportedButton(); return; } // Voice Button Hold if(buttonID == 'voice-button' && actionType == 'hold') { // no special behaviors known for this return; } // Sleep Button Click if(buttonID == 'sleep-button' && actionType == 'click') { if(deviceFamily == 'roku') { _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: 'sleep', num_repeats: 1, delay_secs: 0, hold_secs: 0}); return; } else { unsupportedButton(); return; } } // Sleep Button Hold if(buttonID == 'sleep-button' && actionType == 'hold') { // no special behaviors known for this return; } // PatchWall Button Click if(buttonID == 'patchwall-button' && actionType == 'click') { if(['xiaomi'].includes(deviceFamily)) { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell am start com.mitv.tvhome.atv'}); } else { unsupportedButton(); } return; } // PatchWall Button Hold if(buttonID == 'patchwall-button' && actionType == 'hold') { // no special behaviors known for this - I'm unable to test in my region return; } // Input Button Click if(buttonID == 'input-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); } else { _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell input keyevent 178'}); } return; } // Input Button Hold if(buttonID == 'input-button' && actionType == 'hold') { // no special behaviors known for this return; } // Reboot Button Click if(buttonID == 'reboot-button' && actionType == 'click') { if(['apple-tv', 'roku'].includes(deviceFamily)) { unsupportedButton(); return; } if(confirm('Are you sure you want to reboot '+_hass.states[_config.entity].attributes.friendly_name) == false) { return; } _hass.callService("androidtv", "adb_command", { entity_id: _config.entity, command: 'adb shell reboot' }); return; } // Reboot Button Hold if(buttonID == 'reboot-button' && actionType == 'hold') { // no special actions imagined for this yet return; } // Search Button Click (Apple TV Remote style 2) if(buttonID == 'search-button' && actionType == 'click') { if(['apple-tv'].includes(deviceFamily)) { _hass.callService("media_player", "select_source", { entity_id: _config.entity, source: "Search"}); } else { unsupportedButton(); } return; } // Search Button Hold (Apple TV Remote style 2) if(buttonID == 'search-button' && actionType == 'hold') { // no special behaviors known for this - I do not own this, so I'm unable to test return; } // TODO: Not important, but also not working with the refactor // // Roku Function: Secret Screen Click // if(buttonID == 'roku-secret-screen-button' && actionType == 'click') { // if(['roku'].includes(deviceFamily)) { // var command = ['home', 'home', 'home', 'home', 'home', 'forward', 'forward', 'forward', 'reverse', 'reverse']; // for (let index = 0, len = command.length; index < len; ++index) { // setTimeout(() => // _hass.callService("remote", "send_command", { entity_id: _config.roku_remote_entity, command: command[index], num_repeats: 1, delay_secs: 0, hold_secs: 0}), // index*50); // } // } // else { // unsupportedButton(); // } // return; // } // // Roku Function: Secret Screen Hold // if(buttonID == 'roku-secret-screen-button' && actionType == 'hold') { // // no special actions imagined for this yet // return; // } // uncaught button presses land here //console.log('unhandled '+actionType+' action for '+buttonID); return; } else { // unhandled actiontype return; } } } } customElements.define('firemote-card', FiremoteCard); // Allow this card to appear in the card chooser menu window.customCards = window.customCards || []; window.customCards.push({ type: "firemote-card", name: "Firemote Card", preview: true, description: "Remote control card for Amazon FireTV, Apple TV, Chromecast, NVIDIA Shield, onn., Roku, and Xiaomi devices" }); // Ceate and register the card editor class FiremoteCardEditor extends LitElement { static get properties() { return { hass: {}, _config: {}, }; } // setConfig works the same way as for the card itself setConfig(config) { this._config = config; } // This function is called when the input element of the editor loses focus or is changed configChanged(ev) { const _config = Object.assign({}, this._config); _config[ev.target.name.toString()] = ev.target.value; this._config = _config; if (ev.type == 'change') { // If the Device Family changes, we need to blank the device type value, or // if a boolean style checkbox value changes, special handling is required switch(ev.target.id) { case 'device_family': this._config.device_type = null; break; case 'hideFramesCheckbox': this._config.hide_button_group_frame = ev.target.checked; break; case 'useCustomSkinCheckbox': this._config.useCustomSkin = ev.target.checked; break; case "showMediaControlsCheckbox": this._config.show_media_controls = ev.target.checked; break; } } // A config-changed event will tell lovelace we have made a change to the configuration // this will make sure the changes are saved correctly later and will update the preview const event = new CustomEvent("config-changed", { detail: { config: _config }, bubbles: true, composed: true, }); this.dispatchEvent(event); } translateToUsrLang(englishString) { const ha_language = this.hass.config.language; var translatedString = englishString; if (typeof translationmap.get(ha_language) !== 'undefined'){ if (typeof translationmap.get(ha_language)[englishString] !== 'undefined'){ translatedString = translationmap.get(ha_language)[englishString]; } } return translatedString; } removeMatching(originalArray, regex) { var j = 0; while (j < originalArray.length) { if (regex.test(originalArray[j])) originalArray.splice(j, 1); else j++; } return originalArray; } getEntitiesByType(type) { return Object.keys(this.hass.states).filter( (eid) => eid.substr(0, eid.indexOf('.')) === type ); } getEntitiesByPlatform(platformName) { return Object.keys(this.hass.entities).filter( (eid) => this.hass.entities[eid].platform === platformName ); } getMediaPlayerEntitiesByPlatform(platformName) { var entities = Object.keys(this.hass.entities).filter( (eid) => this.hass.entities[eid].platform === platformName ); const re = /media_player/; return entities.filter(a => re.exec(a)); } getRemoteEntitiesByPlatform(platformName) { //console.log(this.hass.entities); var entities = Object.keys(this.hass.entities).filter( (eid) => this.hass.entities[eid].platform === platformName ); const re = /remote/; const negreg = /Supports/i; return this.removeMatching(entities.filter(a => re.exec(a)), negreg); } getDeviceFamiliesDropdown(optionvalue){ var familykeys = []; for(var [key, value] of devicemap.entries()) { familykeys.push(key) } return html `
`; } getMediaPlayerEntityDropdown(optionValue){ if(['none'].includes(this._config.device_family)) { this._config.entity = 'none'; this.configChanged; return; } var mediaPlayerEntities = []; var heading = ''; if(this._config.device_family == 'apple-tv') { mediaPlayerEntities = this.getMediaPlayerEntitiesByPlatform('apple_tv'); heading = 'Apple TV Media Player '+ this.translateToUsrLang('Entity'); heading = this.hass.config.language == 'he' || this.hass.config.language == 'fr' ? this.translateToUsrLang('Entity') + ' Apple TV Media Player' : 'Apple TV Media Player '+ this.translateToUsrLang('Entity'); } else if(this._config.device_family == 'roku') { mediaPlayerEntities = this.getMediaPlayerEntitiesByPlatform('roku'); heading = this.hass.config.language == 'he' || this.hass.config.language == 'fr' ? this.translateToUsrLang('Entity') + ' Roku Media Player' : 'Roku Media Player '+ this.translateToUsrLang('Entity'); } else { mediaPlayerEntities = this.getMediaPlayerEntitiesByPlatform('androidtv'); heading = this.hass.config.language == 'he' || this.hass.config.language == 'fr' ? this.translateToUsrLang('Entity') + ' Android Debug Bridge' : 'Android Debug Bridge '+ this.translateToUsrLang('Entity'); } var blankEntity = ''; if(this._config.entity == '' || !(mediaPlayerEntities).includes(optionValue)) { blankEntity = html ` `; } return html` ${heading}:


` } getAssociatedRemoteEntityDropdown(optionValue){ if(['amazon-fire', 'none'].includes(this._config.device_family)) { return; } var blankRemoteEntity = ''; var remoteEntities = []; var appleTVRemoteEntities = this.getRemoteEntitiesByPlatform('apple_tv'); if(this._config.device_family == 'apple-tv') { var dropdownLabel = this.translateToUsrLang('Associated') + ' Apple TV Remote ' + this.translateToUsrLang('Entity'); dropdownLabel = this.hass.config.language == 'he' ? 'ישות משויכת ל-' + 'Apple TV Remote ' : dropdownLabel; if(this._config.apple_tv_remote_entity == '' || typeof this._config.apple_tv_remote_entity == 'undefined') { blankRemoteEntity = html ` `; } remoteEntities = this.getRemoteEntitiesByPlatform('apple_tv'); return html` ${dropdownLabel}:


` } if(this._config.device_family == 'roku') { var dropdownLabel = this.translateToUsrLang('Associated') + ' Roku Remote ' + this.translateToUsrLang('Entity'); dropdownLabel = this.hass.config.language == 'he' ? 'ישות משויכת ל-' + 'Roku Remote ' : dropdownLabel; if(this._config.roku_remote_entity == '' || typeof this._config.roku_remote_entity == 'undefined') { blankRemoteEntity = html ` `; } remoteEntities = this.getRemoteEntitiesByPlatform('roku'); return html` ${dropdownLabel}:


` } else { var dropdownLabel = this.translateToUsrLang('Associated') + ' Android TV Remote ' + this.translateToUsrLang('Entity') + ': (' + this.translateToUsrLang('optional') + ')'; dropdownLabel = this.hass.config.language == 'he' ? 'ישות משויכת ל-' + 'Android TV Remote ' : dropdownLabel; dropdownLabel = this.hass.config.language == 'fr' ? 'Entité Android TV Remote Associé (optionnel)' : dropdownLabel; if(this._config.androidTVRemoteEntity == '' || typeof this._config.androidTVRemoteEntity == 'undefined') { blankRemoteEntity = html ` `; } remoteEntities = this.getRemoteEntitiesByPlatform('androidtv_remote'); return html` ${dropdownLabel}


` } } getDeviceTypeDropdown(optionValue, dropdownLabel){ if (this._config.device_family === 'none') { this._config.device_type = 'other'; this._config.entity = 'none'; delete this._config['apple_tv_remote_entity']; delete this._config['android_tv_remote_entity']; this.configChanged; return; } var family = this._config.device_family; var optionMenu = String(); Object.entries(devices).forEach(deviceFamily => { const [familyKey,familyValue] = deviceFamily; if(familyKey == family) { var blankWasDisplayed = false; Object.entries(familyValue).forEach(deviceCategory => { const [categorykey,categoryvalue] = deviceCategory; if(categorykey == 'meta') {return} if(optionValue in categoryvalue) {blankWasDisplayed = true} if(!(optionValue in categoryvalue) && !(blankWasDisplayed)){ optionMenu += ''; blankWasDisplayed = true; } if(categorykey != 'noCategory'){optionMenu += ''} Object.entries(categoryvalue).forEach(deviceEntry => { const [devicekey,deviceproperties] = deviceEntry; if(deviceproperties.supported) { if(devicekey == this._config.device_type) { optionMenu += ''; } else { optionMenu += ''; } } else { optionMenu += ''; } }) if(categorykey != 'noCategory'){optionMenu += ''} }) } }) return html ` ${dropdownLabel}:


`; } getCompatibilityModeDropdown(optionValue, deviceFriendlyName){ if(['apple-tv', 'chromecast', 'homatics', 'nvidia-shield', 'onn', 'roku', 'none'].includes(this._config.device_family)) { return; } if(['xiaomi-tv-stick-4k', 'fire_tv_stick_4k_max_second_gen', 'fire_tv_stick_4k_second_gen'].includes(this._config.device_type)) { return; } var heading = this.translateToUsrLang('Compatibility Mode'); return html` ${heading}:


`; } getAppChoiceOptionMenus(remoteStyle) { if(['none'].includes(this._config.device_family)) { return; } var family = this._config.device_family; if(appButtonMax[remoteStyle]) { var appkeys = []; for (var [key, value] of appmap.entries()) { appkeys.push(key) } const optionsmap = new Map(); for(let i=1; i<=appButtonMax[remoteStyle]; i++) { optionsmap.set(i, "app_launch_"+i); } var optionkeys = []; for (var [key, value] of optionsmap.entries()) { optionkeys.push(key) } return html `
${optionkeys.map((optionnumber) => { var blankOption = html ``; if(!(appmap.has(optionvalue))){ blankOption = html ``; } var heading = this.translateToUsrLang('App Launch Button'); var optionvalue = this._config[optionsmap.get(optionnumber)]; return html ` ${heading} ${optionnumber}:
`; })} `; } } getALConfigOptions(remoteStyle){ if(remoteStyle && remoteStyle=="AL1"){ return html `



`; } if(remoteStyle && remoteStyle=="AL2"){ return html `



${this.translateToUsrLang('DPad Style')}:



`; } } getChromecastMediaControls(remoteStyle) { if (['CC1', 'CC2', 'CC3'].includes(remoteStyle)) { return html`
`; } } render() { if (!this.hass || !this._config) { return html``; } if(!this._config.device_family) { this._config.device_family = devicemap.keys().next().value; } var appLauncherRelativeScaleSlide = ''; if(this._config.defaultRemoteStyle_override == 'AL2') { if(!(this._config.app_launcher_relative_size)) { this._config.app_launcher_relative_size = 0; } appLauncherRelativeScaleSlide = html `

`; } // Get current device's Attributes AND use any applicable overrides from user conf var confRef = this._config; function getDeviceAttribute(deviceAttribute){ return deviceAttributeQuery(deviceAttribute, confRef); } // Rebuild AppMap - allow hdmi inputs where appropriate & add configured custom launchers from YAML refreshAppMap(this._config, getDeviceAttribute('hdmiInputs'), getDeviceAttribute('avInputs'), getDeviceAttribute('tuner')); var deviceModelSelectorLabel = devicemap.get(this._config.device_family).meta.friendlyName + ' ' + this.translateToUsrLang('Device Model'); if (this.hass.config.language == 'he') { deviceModelSelectorLabel = this.translateToUsrLang('Device Model') + ' ' + devicemap.get(this._config.device_family).meta.friendlyName; } if(this._config.device_type == null || this._config.device_type == '') { return html` ${this.translateToUsrLang('Device Family')}:
${this.getDeviceFamiliesDropdown(this._config.device_family)}
${this.getDeviceTypeDropdown(this._config.device_type, deviceModelSelectorLabel)}`; } else { return html` ${this.translateToUsrLang('Device Family')}:
${this.getDeviceFamiliesDropdown(this._config.device_family)}
${this.getDeviceTypeDropdown(this._config.device_type, deviceModelSelectorLabel)} ${this.getMediaPlayerEntityDropdown(this._config.entity)} ${this.getAssociatedRemoteEntityDropdown(this._config.android_tv_remote_entity)}


${appLauncherRelativeScaleSlide}
${this.translateToUsrLang('Remote Style')}:


${this.getChromecastMediaControls(getDeviceAttribute("defaultRemoteStyle"))} ${this.getCompatibilityModeDropdown(this._config.compatibility_mode, getDeviceAttribute('friendlyName'))} ${this.getAppChoiceOptionMenus(getDeviceAttribute("defaultRemoteStyle"))}


${this.getALConfigOptions(getDeviceAttribute("defaultRemoteStyle"))}

${this.translateToUsrLang('Name Position')}:


`; } } } customElements.define("firemote-card-editor", FiremoteCardEditor);