Advertisement
Gubenote

Untitled

Dec 15th, 2018
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
YAML 148.85 KB | None | 0 0
  1. <!-- CUSTOM ALARM COMPONENT PANEL
  2.   https://github.com/gazoscalvertos/Hass-Custom-Alarm
  3.   VERSION: 1.3.4
  4.   MODIFIED: 27/11/18
  5.  
  6.   CHANGE LOG:
  7.  -added basic error message
  8.   -fixed margin issue in Firefox
  9.   -sorted sensors alphabetically
  10.   -Fixed clock, serif, weather, passcode display issues
  11.  
  12.   TODO:
  13.  -local styles to css
  14.   -logsize display count
  15.   -design themes
  16.   -ha default theme
  17.   -disable animations
  18.  
  19.   BUGS
  20.   -Menu resize width on click
  21.   -checkbox style
  22.   -on first start log is in reverse Border-log size not being read
  23.  
  24.   CONSIDERED CHANGE
  25.   -Move from custom alarm to security panel?
  26.   -Rename panel
  27.  
  28. -->
  29. <script>
  30. {
  31.   // show the version of your custom UI in the HA dev info panel (HA 0.66.0+):
  32.  const _NAME = 'Security Panel';
  33.   const _URL = 'https://github.com/gazoscalvertos/Hass-Custom-Alarm/';
  34.   const _VERSION = '27/11/18 v1.3.4';
  35.  
  36.   if (!window.CUSTOM_UI_LIST) window.CUSTOM_UI_LIST = [];
  37.   window.CUSTOM_UI_LIST.push({
  38.     name: _NAME,
  39.     url: _URL,
  40.     version: _VERSION
  41.   });
  42. }
  43. </script>
  44. <script src='/local/lib/jquery-3.2.1.min.js'></script>
  45. <script src='/local/lib/sha256.js'></script>
  46. <script src='/local/lib/countdown360.js'></script>
  47. <script src="/local/lib/jscolor.js"></script>
  48. <link rel='import' href='/local/alarm/custom-element.html'>
  49. <style>
  50.  @font-face {
  51.     font-family: 'Lobster';
  52.     font-style: normal;
  53.     font-weight: 400;
  54.     src: local('Lobster Regular'), local('Lobster-Regular'), url(/local/alarm/lobster.woff2) format('woff2');
  55.     unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
  56.   }
  57.   @import url('./local/alarm/alarm.css');
  58. </style>
  59. <dom-module id='ha-panel-alarm'>
  60.  <template>
  61.    <link rel='stylesheet' href='/local/alarm/alarm.css'>
  62.     <style include='ha-style iron-flex iron-flex-factors'>
  63.    </style>
  64.     <ha-app-layout has-scrolling-region id='layout'>
  65.      <app-header slot='header' fixed>
  66.        <app-toolbar>
  67.          <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  68.            <ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
  69.          </template>
  70.          <div id='main-title-bar'>
  71.            <template is='dom-if' if='[[alarm.attributes.settings]]'>
  72.              <div></div>
  73.            </template>
  74.             <div main-title id='main-title-text'>[[panel_title]]</div>
  75.            <template is='dom-if' if='[[alarm.attributes.settings]]'>
  76.              <div id='settings-icon'><iron-icon on-click='toggleSettings' icon='mdi:settings'></iron-icon></div>
  77.            </template>
  78.          </div>
  79.        </app-toolbar>
  80.      </app-header>
  81.      <div id='content'>
  82.        <div id='alarm-panel' >
  83.          <div id='status-bar' class='horizontal layout center-justified'>
  84.             <div id='clock' class='box box-header' >
  85.               <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_clock)]]'>
  86.                 <div id='time' class='time hours shadow'>
  87.                   <span>[[time]]</span>
  88.                 </div>
  89.               </template>
  90.               <div id='countdown' class='countdown-timer'></div>
  91.             </div>
  92.  
  93.             <div id='alarm-status' class='box box-header'>
  94.               <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  95.                 <span class='shadow'>Desactivada</span>
  96.               </template>
  97.               <template is='dom-if' if='[[!alarm.attributes.panel_locked]]'>
  98.                 <template is='dom-if' if='[[!isdisarmed(alarm)]]'>
  99.                   <span class='shadow'>[[computeLabel(alarm.state)]]</span>
  100.                 </template>
  101.               </template>
  102.  
  103.               <template is='dom-if' if='[[showCodePanel(alarm)]]'>
  104.                 <template is='dom-if' if='[[alarm.attributes.panel_locked]]'>
  105.                   <span class='shadow'>PANEL LOCKED OUT</span>
  106.                 </template>
  107.               </template>
  108.             </div>
  109.  
  110.             <div id='weather' class='box box-header weather'>
  111.               <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_weather)]]'>
  112.                   <div class='weather-summary shadow'>
  113.                     <span>[[temp]] &#176;C</span>
  114.                   </div>
  115.                   <div id='weather-icon' class='weather-summary'>
  116.                     <object id='weather_svg' class='weather-summary' type='image/svg+xml' data='[[weather.attributes.entity_picture]]'></object>
  117.                   </div>
  118.                   <div class='weather-summary shadow'>
  119.                     <span>[[weather.state]]</span>
  120.                   </div>
  121.               </template>
  122.             </div>
  123.           </div>
  124.  
  125.           <div id='main-content'>
  126.  
  127.             <div id='carousel_main' class='content-box'>
  128.               <!-- ###########################################CAROUSEL MAIN############################################### -->
  129.               <div id='carousel_template' class='carousel-cell carousel-slide-in' style='height: 50vh'>
  130.  
  131.               </div>
  132.  
  133.               <!-- ###########################################CONTROLS############################################### -->
  134.               <div id='controls' class='carousel-cell remove'>
  135.                 <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  136.                   <div class='vLayout arm'>
  137.                     <template is='dom-if' if='[[!showCodePanel(alarm)]]'>
  138.                       <template is='dom-if' if='[[!attemptedArm]]'>
  139.                         <template is='dom-if' if='[[alarm.attributes.enable_perimeter_mode]]'>
  140.                           <paper-button id='arm-perimeter' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_night'>Silencioso</paper-button>
  141.                         </template>
  142.                         <paper-button id='arm-home' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_home' raised>Casa</paper-button>
  143.                         <paper-button id='arm-away' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_away'>Fuera de Casa</paper-button>
  144.  
  145.                       </template>
  146.                     </template>
  147.                     <template is='dom-if' if='[[attemptedArm]]'>
  148.                       <div class='vertical layout'>
  149.                         <div style='text-align: center;'>
  150.                           <h1>Sensores Abiertos</h1>
  151.                         </div>
  152.                         <div class='horizontal layout'>
  153.                           <paper-button id='arm-override' class='override' on-click='callAlarmService' data-call='override' data-arm='attemptArmMode'>SALTAR</paper-button>
  154.                           <paper-button id='arm-cancel' class='cancel' on-click='callAlarmService' data-call='cancel'>CANCELAR</paper-button>
  155.                         </div>
  156.                       </div>
  157.                     </template>
  158.                   </div>
  159.                 </template>
  160.  
  161.                 <template is='dom-if' if='[[showCodePanel(alarm)]]'>
  162.  
  163.                   <template is='dom-if' if='[[!attemptedArm]]'>
  164.  
  165.                     <template is='dom-if' if='[[requiresCode()]]'>
  166.                       <template is='dom-if' if='[[!alarm.attributes.panel_locked]]'>
  167.                           <div id='code-display'>[[display_code]]</div>
  168.                       </template>
  169.                     </template>
  170.  
  171.                     <div id='codepanel' class='horizontal layout center-justified'>
  172.                       <div>
  173.                         <div class='digits horizontal layout' style='justify-content: flex-start;'>
  174.                           <template is='dom-if' if='[[requiresCode()]]'>
  175.                             <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  176.                               <template is='dom-if' if='[[alarm.attributes.enable_perimeter_mode]]'>
  177.                                 <paper-button id='arm-perimeter1' class='action-button' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_night'>Silencioso</paper-button>
  178.                               </template>
  179.                             </template>
  180.                             <paper-button on-click='callcode' data-digit=1 raised>1</paper-button>
  181.                             <paper-button on-click='callcode' data-digit=2 raised>2</paper-button>
  182.                             <paper-button on-click='callcode' data-digit=3 raised>3</paper-button>
  183.                             <paper-button class='sm action-button clear' on-click='callclearcode' raised>Cancelar</paper-button>
  184.                           </template>
  185.  
  186.                         </div>
  187.                         <div class='digits horizontal layout center-justified'>
  188.                           <template is='dom-if' if='[[requiresCode()]]'>
  189.                             <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  190.                               <paper-button id='arm-home1' class='action-button' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_home' raised>Casa</paper-button>
  191.                             </template>
  192.                             <paper-button on-click='callcode' data-digit=4 raised>4</paper-button>
  193.                             <paper-button on-click='callcode' data-digit=5 raised>5</paper-button>
  194.                             <paper-button on-click='callcode' data-digit=6 raised>6</paper-button>
  195.                             <paper-button class='sm'  on-click='callcode' data-digit=0 raised>0</paper-button>
  196.  
  197.                           </template>
  198.                         </div>
  199.                         <div class='digits horizontal layout'>
  200.                           <template is='dom-if' if='[[requiresCode()]]'>
  201.                             <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  202.                               <paper-button id='arm-away1' class='action-button' on-click='callAlarmService' data-call='arm' data-arm='alarm_arm_away'>Fuera de Casa</paper-button>
  203.                             </template>
  204.                             <paper-button on-click='callcode' data-digit=7 raised>7</paper-button>
  205.                             <paper-button on-click='callcode' data-digit=8 raised>8</paper-button>
  206.                             <paper-button on-click='callcode' data-digit=9 raised>9</paper-button>
  207.                           </template>
  208.                           <template is='dom-if' if='[[!isdisarmed(alarm)]]'>
  209.                             <paper-button class='sm action-button disarm' on-click='callAlarmService' data-call='disarm' raised>Desarmar</paper-button>
  210.                               </template>
  211.                         </div>
  212.                         <div class='xs digits horizontal layout center-justified'>
  213.                           <paper-button class='clear action-button' on-click='callclearcode' raised>C</paper-button>
  214.                           <paper-button on-click='callcode' data-digit=0 raised>0</paper-button>
  215.                           <template is='dom-if' if='[[!isdisarmed(alarm)]]'>
  216.                             <paper-icon-button class='disarm action-button' on-click='callAlarmService' data-call='disarm' icon='mdi:fingerprint' raised></paper-icon-button>
  217.                           </template>
  218.                         </div>
  219.                       </div>
  220.                     </div>
  221.                   </template>
  222.                 </template>
  223.               </div>
  224.  
  225.               <!-- ###########################################FLOOR PLAN############################################### -->
  226.  
  227.                 <div id='floorplan' class='carousel-cell remove'>
  228.                   <template is='dom-if' if='{{alarm.attributes.panel.enable_floorplan_panel}}' >
  229.                   <div>
  230.                     <div class='title'>Plano de casa</div>
  231.                     <state-card-content state-obj='[[getObject(alarm.attributes.panel.floorplan)]]' hass='[[hass]]'></state-card-content>
  232.                   </div>
  233.                 </template>
  234.               </div>
  235.  
  236.               <!-- ########################################### SENSORS############################################### -->
  237.               <div id='sensors' class='carousel-cell remove'>
  238.                 <!-- ###########################################OPEN SENSORS############################################### -->
  239.                 <template is='dom-if' if='[[opencount]]'>
  240.                   <div>
  241.                     <div class='openSensors'>Sensores activos</div>
  242.                         <div class='sensors'>
  243.                         <template is='dom-repeat' items='[[opensensors(alarm, allsensors)]]' as='entity'>
  244.                           <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  245.                         </template>
  246.                       </div>
  247.                   </div>
  248.                 </template>
  249.  
  250.                 <!-- ###########################################ARMED############################################## -->
  251.                 <template is='dom-if' if='[[!isdisarmed(alarm)]]'>
  252.  
  253.                   <div >
  254.                     <template is='dom-if' if='[[computeArray(delayed)]]'>
  255.                       <div class='box-sensors'>
  256.                         <div class='sensor-title'>Sensores temporizados</div>
  257.                         <div class='sensors'>
  258.                           <template is='dom-repeat' items='[[delayed]]' as='entity'>
  259.                             <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  260.                           </template>
  261.                         </div>
  262.                       </div>
  263.                     </template>
  264.  
  265.                     <template is='dom-if' if='[[computeArray(immediate)]'>
  266.                       <div class='box-sensors'>
  267.                         <div class='sensor-title'>Sensores inmediatos</div>
  268.                         <div class='sensors'>
  269.                           <template is='dom-repeat' items='[[immediate]]' as='entity'>
  270.                             <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  271.                           </template>
  272.                         </div>
  273.                       </div>
  274.                     </template>
  275.                   </div>
  276.  
  277.                    <div class='box-sensors'>
  278.                      <template is='dom-if' if='[[computeArray(ignored)]]'>
  279.                       <div class='sensor-title'>Sensores Inactivos</div>
  280.                       <div class='sensors'>
  281.                         <template is='dom-repeat' items='[[ignored]]' as='entity'>
  282.                           <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  283.                         </template>
  284.                       </div>
  285.                     </template>
  286.                   </div>
  287.                 </template>
  288.  
  289.                 <!-- ###########################################DISARMED############################################### -->
  290.                 <template is='dom-if' if='[[isdisarmed(alarm)]]'>
  291.                   <div class='box-sensors'>
  292.                     <div class='sensor-title'>Todos los sensores</div>
  293.                     <div class='sensors'>
  294.                       <template is='dom-repeat' items='[[opensensors(alarm, allsensors)]]' as='entity'>
  295.                         <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  296.                       </template>
  297.                       <template is='dom-repeat' items='[[closedsensors(alarm, allsensors)]]' as='entity'>
  298.                         <state-card-display state-obj='[[entity]]' hass='[[hass]]' class='tappable' on-tap='entityTapped' data-entity='[[entity.entity_id]]'></state-card-display>
  299.                       </template>
  300.                     </div>
  301.                   </div>
  302.                 </template>
  303.               </div>
  304.  
  305.               <!-- ###########################################CAMERAS############################################### -->
  306.               <div id='cameras' class='carousel-cell remove'>
  307.                 <div class='title' style='padding-bottom: 10px'>Cámaras</div>
  308.                 <div class='info-detail' style='text-align: center; padding-bottom: 5px;'>Cámaras actualizadas cada [[alarm.attributes.panel.camera_update_interval]] segundos</div>
  309.                 <div class=' vLayout'>
  310.                   <template is='dom-repeat' items='[[alarm.attributes.panel.cameras]]' as='camera'>
  311.                     <div class='cameras' >
  312.                          <img src='[[updateCameraFeedSrc(camera, camera_time)]]' class='camera camera-feed' alt='[[computeFriendlyName(camera)]]'>
  313.                         <div class='camera-caption'>
  314.                           [[computeFriendlyName(camera)]]
  315.                       </div>
  316.                     </div>
  317.                   </template>
  318.                 </div>
  319.                 </div>
  320.  
  321.               <!-- ###########################################ACTIVITY LOG############################################### -->
  322.               <div id='log' class='carousel-cell remove'>
  323.                 <template is='dom-if' if='[[alarm.attributes.enable_log]]' >
  324.                     <div class='title'>Actividad</div>
  325.                         <div >
  326.                         <template is='dom-repeat' items='[[reverseArray(alarm.attributes.logs, 10)]]' as='entry'>
  327.                           <div style='display:flex; padding: 8px;'>
  328.                             <div>
  329.                               <state-badge id='icon' style$='background-image: url([[computeLog(entry, "image")]]);'></state-badge>
  330.                             </div>
  331.                             <div class='log-item'>
  332.                               <div style='color: var(--info-detail-text-color); margin-right: 5px;'>[[computeLog(entry,'name')]]</div>
  333.                               <div>[[computeLog(entry,'message')]]</div>
  334.                               <span></span>
  335.                               <div class='log-item-date' style='color: var(--info-detail-text-color);'>[[computeLog(entry,'time')]]</div>
  336.                             </div>
  337.                           </div>
  338.                         </template>
  339.                       </div>
  340.                 </template>
  341.               </div>
  342.  
  343.                 <!-- ###########################################CUSTOM PANEL############################################### -->
  344.                 <div id='custom' class='carousel-cell remove'>
  345.                   <template is='dom-if' if='{{alarm.attributes.panel.enable_custom_panel}}' >
  346.                   <custom-element is-panel></custom-element>
  347.                 </template>
  348.               </div>
  349.  
  350.  
  351.               <!-- ########################################################################### SETTINGS ##################################################################################################### -->
  352.                 <div id='settings' class='carousel-cell' style='left: 0 !important;' >
  353.                   <!-- <alarm-settings is-panel></alarm-settings> -->
  354.  
  355.                 <!-- ###########################################SETTINGS SELECTION BAR############################################### -->
  356.  
  357.                 <template is='dom-if' if='[[settingsUnlock]]' >
  358.                   <div class='title'>Settings</div>
  359.                   <div id='settings-nav' class='horizontal layout'>
  360.                     <div class='sm horizontal layout'>
  361.                       <div class='settings-nav-inner vertical layout'>
  362.                         <paper-icon-button icon='mdi:information-outline' title='Info' on-click='carouselClick' data-selection='settings-info'></paper-icon-button>
  363.                         <div class='settings-nav-text'>About</div>
  364.                       </div>
  365.                       <div class='settings-nav-inner vertical layout' >
  366.                         <paper-icon-button icon='mdi:brush' title='Design' on-click='carouselClick' data-selection='settings-all'></paper-icon-button>
  367.                         <div class='settings-nav-text'>Design</div>
  368.                       </div>
  369.                       <div class='settings-nav-inner vertical layout' >
  370.                         <paper-icon-button icon='mdi:dialpad' title='Alarm' on-click='carouselClick' data-selection='settings-alarm'></paper-icon-button>
  371.                         <div class='settings-nav-text'>Alarm</div>
  372.                       </div>
  373.                       <div class='settings-nav-inner vertical layout' >
  374.                         <paper-icon-button icon='mdi:radio-tower' title='Alarm Sensors' on-click='carouselClick' data-selection='settings-sensors'></paper-icon-button>
  375.                         <div class='settings-nav-text'>Sensors</div>
  376.                       </div>
  377.                       <div class='settings-nav-inner vertical layout' >
  378.                         <paper-icon-button icon='mdi:floor-plan' title='Floorplan' on-click='carouselClick' data-selection='settings-floorplan'></paper-icon-button>
  379.                         <div class='settings-nav-text'>Floorplan</div>
  380.                       </div>
  381.                       <div class='settings-nav-inner vertical layout' >
  382.                         <paper-icon-button icon='mdi:cctv' title='Camera' on-click='carouselClick' data-selection='settings-cameras'></paper-icon-button>
  383.                         <div class='settings-nav-text'>Cameras</div>
  384.                       </div>
  385.                       <div class='settings-nav-inner vertical layout' >
  386.                         <paper-icon-button icon='mdi:access-point' title='MQTT' on-click='carouselClick' data-selection='settings-mqtt'></paper-icon-button>
  387.                         <div class='settings-nav-text'>MQTT</div>
  388.                       </div>
  389.                       <div class='settings-nav-inner vertical layout' >
  390.                         <paper-icon-button icon='mdi:atom' title='Custom Panel' on-click='carouselClick' data-selection='settings-custom'></paper-icon-button>
  391.                         <div class='settings-nav-text'>Custom</div>
  392.                       </div>
  393.                       <!-- <div class='settings-nav-inner vertical layout' >
  394.                         <paper-icon-button icon='mdi:help' title='Help Guide' on-click='carouselClick' data-selection='settings-help'></paper-icon-button>
  395.                         <div class='settings-nav-text'>Help</div>
  396.                       </div> -->
  397.                       <div class='settings-nav-inner vertical layout' >
  398.                         <paper-icon-button icon='mdi:logout' title='Logout' on-click='carouselClick' data-selection='set-login'></paper-icon-button>
  399.                         <div class='settings-nav-text'>Logout</div>
  400.                       </div>
  401.                     </div>
  402.  
  403.                     <paper-dropdown-menu class='xs dropdown-content' label="Settings">
  404.                       <paper-listbox slot="dropdown-content" >
  405.                         <paper-item on-click='carouselClick' data-selection='settings-info'><iron-icon icon='mdi:information-outline'></iron-icon>About</paper-item>
  406.                         <paper-item on-click='carouselClick' data-selection='settings-all'><iron-icon icon='mdi:brush'></iron-icon>Design</paper-item>
  407.                           <paper-item on-click='carouselClick' data-selection='settings-alarm'><iron-icon icon='mdi:dialpad'></iron-icon>Alarm</paper-item>
  408.                           <paper-item on-click='carouselClick' data-selection='settings-sensors'><iron-icon icon='mdi:radio-tower'></iron-icon>Sensors</paper-item>
  409.                         <paper-item on-click='carouselClick' data-selection='settings-floorplan'><iron-icon icon='mdi:floor-plan'></iron-icon>Floorplan</paper-item>
  410.                         <paper-item on-click='carouselClick' data-selection='settings-cameras'><iron-icon icon='mdi:cctv'></iron-icon>Cameras</paper-item>
  411.                           <paper-item on-click='carouselClick' data-selection='settings-mqtt'><iron-icon icon='mdi:access-point'></iron-icon>MQTT</paper-item>
  412.                           <paper-item on-click='carouselClick' data-selection='settings-custom'><iron-icon icon='mdi:atom'></iron-icon>Custom</paper-item>
  413.                           <!-- <paper-item on-click='carouselClick' data-selection='settings-help'><iron-icon icon='mdi:help'></iron-icon>Help</paper-item> -->
  414.                         </paper-listbox>
  415.                     </paper-dropdown-menu>
  416.                   </div>
  417.                 </template>
  418.  
  419.                 <div id='carousel_settings'>
  420.  
  421.                   <!-- ########################################################################### LOGIN ##################################################################################################### -->
  422.                   <div id='set-login' class='carousel-cell remove'>
  423.                     <h1>Admin Password</h1>
  424.                     <div class='info-detail'>Enter the admin password defined in your alarm.yaml to access this panel</div>
  425.                     <paper-input class="span11" id="passwordInput" type="password" placeholder="Enter Password..." on-keyup='checkSettingsPassword'></paper-input>
  426.                   </div>
  427.  
  428.                   <!-- ########################################################################### INFO ##################################################################################################### -->
  429.                   <div id='settings-info' class='carousel-cell remove'>
  430.  
  431.                     <div class='setting-outer vertical layout'>
  432.  
  433.                       <h1><iron-icon icon='mdi:heart-pulse' style='padding-right: 8px'></iron-icon>Help Support This Work</h1>
  434.  
  435.                       <span class='info-detail'>Thanks to the community for all the input into this. Consider supporting this project and donate! All funds will go towards bringing new features, hardware support and bug squashing! It is a community effort to make both the alarm and this smart panel feature-rich as well as simple to deploy and use. We need all the help we can get!</span>
  436.                       <div class='setting-inner horizontal layout flexwrap' style='padding-top: 10px;'>
  437.  
  438.                         <div class='vertical layout donate-box'>
  439.                           <div id='paypal' class='donate-paypal'>
  440.                             <a href='https://paypal.me/haCustomAlarm' class='info-detail'>
  441.                               <span class='info-header'>Paypal:</span>
  442.                               <div class='logo'></div>
  443.                               <span class='info-detail'>Donate</span>
  444.                             </a>
  445.                           </div>
  446.                         </div>
  447.  
  448.                         <div class='vertical layout donate-box'>
  449.                           <div id='xrp' class='donate' data-address='rwuMp76ht6dmGvipxwKr5ZE6VpF7ZKC7qs'>
  450.                             <span class='info-header'>Ripple (XRP):</span>
  451.                             <div class='logo'></div>
  452.                           </div>
  453.                           <span class='info-detail'>rwuMp76ht6dmGvipxwKr5ZE6VpF7ZKC7qs</span>
  454.                         </div>
  455.  
  456.                         <div class='vertical layout donate-box'>
  457.                           <div id='btc' class='donate' data-address='1NFeyzpKKiKbBYSmCLQZQLxBqJbhSbqmwd'>
  458.                             <span class='info-header'>Bitcoin (BTC):</span>
  459.                             <div class='logo'></div>
  460.                           </div>
  461.                           <span class='info-detail'>1NFeyzpKKiKbBYSmCLQZQLxBqJbhSbqmwd</span>
  462.                         </div>
  463.  
  464.                         <div class='vertical layout donate-box'>
  465.                           <div id='bch' class='donate' data-address='qzw3frcjnqmetphg25suac5m70348qj80qfjnaje2j'>
  466.                             <span class='info-header'>Bitcoin Cash (BCH):</span>
  467.                             <div class='logo'></div>
  468.                           </div>
  469.                           <span class='info-detail'>qzw3frcjnqmetphg25suac5m70348qj80qfjnaje2j</span>
  470.                         </div>
  471.  
  472.                         <div class='vertical layout donate-box'>
  473.                           <div id='eth' class='donate' data-address='0xCbeD2D2cf0434370c1ca126707009b876b736609'>
  474.                             <span class='info-header'>Ethereum (ETH):</span>
  475.                             <div class='logo'></div>
  476.                           </div>
  477.                           <span class='info-detail'>0xCbeD2D2cf0434370c1ca126707009b876b736609</span>
  478.                         </div>
  479.  
  480.                         <div class='vertical layout donate-box'>
  481.                           <div id='ltc' class='donate' data-address='LTUViN3QUESkQk3mG2hvTzhLRQPVAd269f'>
  482.                             <span class='info-header'>Litecoin (LTC):</span>
  483.                             <div class='logo'></div>
  484.                             </div>
  485.                             <span class='info-detail'>LTUViN3QUESkQk3mG2hvTzhLRQPVAd269f</span>
  486.                         </div>
  487.  
  488.                       </div>
  489.                     </div>
  490.  
  491.                     <div>
  492.                       <h1>Contribute</h1>
  493.                       <a href='https://github.com/gazoscalvertos/Hass-Custom-Alarm/issues'><iron-icon icon='mdi:bug' style='padding-right: 8px'></iron-icon><span>Found a bug or got a feature idea? Let's talk about it on Github</span></a><br>
  494.                       <iron-icon icon='mdi:earth' style='padding-right: 8px'></iron-icon><span>Talk to your friends and colleagues about how awesome this alarm and smart panel is</span><br>
  495.  
  496.                     </div>
  497.  
  498.                     <div>
  499.                       <h1>Your Versions:</h1>
  500.                       <div>
  501.                         <p><span class='info-header'>Home Assistant: </span><span class='info-detail'>v[[hass.config.version]]</span></p>
  502.                         <p><span class='info-header'>This Panel: </span><span class='info-detail'>v[[version]]</span></p>
  503.                         <p><span class='info-header'>Component (Bwalarm): </span><span class='info-detail'>v[[alarm.attributes.bwalarm_version]]</span></p>
  504.                         <p><span class='info-header'>Python: </span><span class='info-detail'>v([[alarm.attributes.py_version]])</span></p>
  505.                       </div>
  506.                     </div>
  507.  
  508.                     <div>
  509.                       <template is='dom-if' if='[[errors]]' >
  510.                         <h1>Issues:</h1>
  511.                         <div>
  512.                           <template is='dom-repeat' items='[[errors]]' as='entity'>
  513.                             <p class='info-detail'>&#8226;  [[entity]]</p>
  514.                           </template>
  515.                         </div>
  516.                       </template>
  517.                     </div>
  518.  
  519.                     <paper-button id='restart' data-type='button' on-click='restartHA'><iron-icon icon='mdi:restart' style='padding-right: 8px'></iron-icon><span>Restart Home Assistant</span></paper-button>
  520.  
  521.                   </div>
  522.  
  523.                   <!-- ########################################################################### @PANEL ##################################################################################################### -->
  524.                   <div id='settings-all' class='carousel-cell remove'>
  525.                     <h1>Panel Interface Related:</h1>
  526.  
  527.                     <div class='setting-outer horizontal layout'>
  528.                       <div class='setting-inner vertical layout'>
  529.                         <span class='info-header'>Admin Password: </span>
  530.                         <span class='info-detail'>Reset the password used to access this settings panel.</span>
  531.                         <div class='setting-input horizontal layout'>
  532.                           <paper-input id='admin_password' type="password" placeholder='Admin Password'></paper-input>
  533.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='admin_password' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  534.                         </div>
  535.                       </div>
  536.                     </div>
  537.  
  538.                     <!-- <div class='setting-outer vertical layout'>
  539.                       <div class='setting-inner horizontal layout'>
  540.                         <span class='info-header'>Disable Animations: </span>
  541.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.disable_animations)}}' on-change='settingsSave' data-setting='panel' data-attribute='disable_animations'></paper-toggle-button>
  542.                       </div>
  543.                       <span class='info-detail'>If enabled, this will disable all animations. This is designed to improve performance on older devices. NOTE you can also disable animations per user within the user section. This is handy to set animations per device.</span>
  544.                     </div> -->
  545.  
  546.                     <div class='setting-outer horizontal layout'>
  547.                       <div class='setting-inner vertical layout'>
  548.                         <span class='info-header'>Alarm Panel Title: </span>
  549.                         <span class='info-detail'>The text that shows on the header bar. Defaults to 'Home Alarm' if not set</span>
  550.                         <div class='setting-input horizontal layout'>
  551.                           <paper-input id='panel_title' value='{{alarm.attributes.panel.panel_title}}' placeholder='Panel Title'></paper-input>
  552.                           <paper-icon-button data-type='input_text' data-setting='panel' data-attribute='panel_title' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  553.                         </div>
  554.                       </div>
  555.                     </div>
  556.  
  557.                     <div class='setting-outer vertical layout'>
  558.                       <div class='setting-inner horizontal layout'>
  559.                         <span class='info-header'>Clock: </span>
  560.                         <paper-toggle-button class='setting-toggle' checked$='{{alarm.attributes.panel.enable_clock}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_clock'></paper-toggle-button>
  561.                       </div>
  562.                       <span class='info-detail'>Enables the clock widget in this panel. A Time Sensor named: 'sensor.time' must exist within your Home Assistant setup</span>
  563.                     </div>
  564.  
  565.                     <div class='setting-outer vertical layout'>
  566.                       <div class='setting-inner horizontal layout'>
  567.                         <span class='info-header'>Clock 12 Hour Mode?: </span>
  568.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.enable_clock_12hr)}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_clock_12hr'></paper-toggle-button>
  569.                       </div>
  570.                       <span class='info-detail'>Changes the clock to 12hour mode when enabled. Default False which shows 24hr mode</span>
  571.                     </div>
  572.  
  573.                     <div class='setting-outer vertical layout'>
  574.                       <div class='setting-inner horizontal layout'>
  575.                         <span class='info-header'>Weather: </span>
  576.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.enable_weather)}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_weather'></paper-toggle-button>
  577.                       </div>
  578.                       <span class='info-detail'>Enables the weather widget in this panel. A Weather Sensor named: 'sensor.weather_summary' or 'sensor.dark_sky_summary' must exist within your Home Assistant setup</span>
  579.                     </div>
  580.  
  581.                     <div class='setting-outer vertical layout'>
  582.                       <div class='setting-inner horizontal layout'>
  583.                         <span class='info-header'>Passcode Hidden: </span>
  584.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.hide_passcode)}}' on-change='settingsSave' data-setting='panel' data-attribute='hide_passcode'></paper-toggle-button>
  585.                       </div>
  586.                       <span class='info-detail'>Masks the passcode within the panel input box when typing</span>
  587.                     </div>
  588.  
  589.                     <div class='setting-outer vertical layout'>
  590.                       <div class='setting-inner horizontal layout'>
  591.                         <span class='info-header'>Hide HA Sidebar When Armed: </span>
  592.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.hide_sidebar)}}' on-change='settingsSave' data-setting='panel' data-attribute='hide_sidebar'></paper-toggle-button>
  593.                       </div>
  594.                       <span class='info-detail'>When the alarm is armed the Home Assistant sidebar will be disabled if enabled. This prevents an intruder simply stopping home assistant from the configuration menu. For this to be truly effective use a browser which locks access to the URL function.</span>
  595.                     </div>
  596.  
  597.                     <div class='setting-outer vertical layout'>
  598.                       <div class='setting-inner horizontal layout'>
  599.                         <span class='info-header'>Round Buttons: </span>
  600.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.round_buttons)}}' on-change='settingsSave' data-setting='panel' data-attribute='round_buttons'></paper-toggle-button>
  601.                       </div>
  602.                       <span class='info-detail'>Choose whether the alarm buttons should be round or not.</span>
  603.                     </div>
  604.  
  605.                     <div class='setting-outer vertical layout'>
  606.                       <div class='setting-inner horizontal layout'>
  607.                         <span class='info-header'>Shadow Text Effect: </span>
  608.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.shadow_effect)}}' on-change='settingsSave' data-setting='panel' data-attribute='shadow_effect'></paper-toggle-button>
  609.                       </div>
  610.                       <span class='info-detail'>Enables the shadow text effect in this panel. Defaults to false</span>
  611.                     </div>
  612.  
  613.                     <div class='setting-outer vertical layout'>
  614.                       <div class='setting-inner horizontal layout'>
  615.                         <span class='info-header'>Serif Font: </span>
  616.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.enable_serif_font)}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_serif_font'></paper-toggle-button>
  617.                       </div>
  618.                       <span class='info-detail'>Enables the 'Lobster' serif font on the title, time and weather within this panel. Default False</span>
  619.                     </div>
  620.  
  621.                     <div>
  622.                       <h1>Themes</h1>
  623.                       <div class='info-detail'>Themes allow you to override the default Home Assistant colors.</div>
  624.  
  625.                       <!-- Themes List [Buttons] {defineColors(themeName), unhide div}  -->
  626.                         <template is='dom-if' if='[[alarm.attributes.themes]]'>
  627.                           <!-- <span class='info-header' style='padding-top: 10px;'>Defined:</span> -->
  628.                           <paper-listbox id='themes-listbox' class='vertical layout'>
  629.                           <template is='dom-repeat' items='[[alarm.attributes.themes]]'>
  630.                             <div class='horizontal layout'>
  631.                               <paper-item on-click='defineColors' data-theme$='[[item.name]]' style='width: 75%;  text-transform: uppercase; text-transform: uppercase;'><iron-icon on-click='defineColors' data-theme$='[[item.name]]' icon='mdi:format-paint' style=' padding-right: 7px;'></iron-icon>[[item.name]]</paper-item>
  632.                               <paper-toggle-button class='setting-toggle' checked$='[[item.active]]' on-change='settingsSave' data-type='boolean' name$='[[item.name]]' data-attribute='activated' data-setting='themes'>Activada</paper-toggle-button>
  633.                               <paper-icon-button on-click='settingsSave' data-setting='themes' name$='[[item.name]]' data-type='delete' icon='mdi:delete' style='margin-left: 20px; color: white;'></paper-icon-button>
  634.                             </div>
  635.                           </template>
  636.                         </paper-listbox>
  637.                       </template>
  638.                       <paper-button on-click='unhideDiv' data-div='#themeDetail' style='background: unset; text-transform: capitalize;'><iron-icon  on-click='unhideDiv' data-div='#themeDetail' icon='mdi:plus-circle-outline' style='padding-right: 7px;'></iron-icon>New</paper-button>
  639.                     </div>
  640.  
  641.                     <!-- /* Theme Name [Input] | Update [Button] | Delete [Button] */ -->
  642.                     <div id='themeDetail' class='remove'>
  643.  
  644.                       <h1>Update Theme</h1>
  645.  
  646.                       <div class='setting-outer horizontal layout'>
  647.                         <div class='setting-inner vertical layout'>
  648.                           <span class='info-header'>Theme Name: </span>
  649.                           <span class='info-detail'>Name of the theme.</span>
  650.                           <div class='setting-input horizontal layout'>
  651.                             <paper-input id='theme-name' type="text" placeholder='Theme Name'></paper-input>
  652.                             <paper-icon-button id='theme-name-button' data-type='input_text' data-attribute='theme-name' data-setting='themes' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  653.                           </div>
  654.                         </div>
  655.                       </div>
  656.  
  657.                       <!-- Time Based Settings [TODO] -->
  658.  
  659.                       <template is='dom-repeat' items='{{settingColors}}' as='color'>
  660.                         <div class='setting-outer vertical layout'>
  661.                           <div class='setting-inner horizontal layout' style='display: block;'>
  662.                             <div style='float: left'>
  663.                               <span class='info-header'>[[color.header]]: </span>
  664.                             </div>
  665.                             <div class='horizontal layout' style='float: right'>
  666.                               <paper-button class='deleteThemeColor' on-click='settingsSave' name$='[[color.theme]]' data-attribute$='[[color.name]]' data-type='clear' data-setting='themes' style='background: unset;'><iron-icon icon='mdi:delete'></iron-icon>Clear?</paper-button>
  667.                               <span class='colPicker' id$='[[color.name]]_span' value='[[color.color]]' style='margin: auto;'></span>
  668.                               <paper-icon-button data-type='color' data-setting='themes' name$='[[color.theme]]' data-attribute$='[[color.name]]' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  669.                             </div>
  670.                           </div>
  671.                           <span class='info-detail'>[[color.description]]</span>
  672.                         </div>
  673.                       </template>
  674.                     </div>
  675.                   </div>
  676.  
  677.                   <!-- ########################################################################### @ALARM ##################################################################################################### -->
  678.                   <div id='settings-alarm' class='carousel-cell remove'>
  679.                     <h1>Alarm</h1>
  680.  
  681.                     <div class='setting-outer vertical layout'>
  682.                       <div class='setting-inner horizontal layout'>
  683.                         <span class='info-header'>Alarm Persistence: </span>
  684.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.enable_persistence}}' on-change='settingsSave' data-setting='root' data-attribute='enable_persistence'></paper-toggle-button>
  685.                       </div>
  686.                       <span class='info-detail'>When enabled the alarm state is persistently saved when eith Home Assistant or the underlying host is restarted. This is useful in the event of power loss. Default False.</span>
  687.                     </div>
  688.  
  689.                     <div class='setting-outer horizontal layout'>
  690.                       <div class='setting-inner vertical layout'>
  691.                         <span class='info-header'>Master Code: </span>
  692.                         <span class='info-detail'>Set/Reset the master passcode to arm/disarm your alarm</span>
  693.                         <div class='setting-input horizontal layout'>
  694.                           <paper-input id='code' type="password" placeholder='Master Passcode'></paper-input>
  695.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='code' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  696.                         </div>
  697.                       </div>
  698.                     </div>
  699.  
  700.                     <div class='setting-outer horizontal layout'>
  701.                       <div class='setting-inner vertical layout'>
  702.                         <span class='info-header'>Panic Code: </span>
  703.                         <span class='info-detail'>Set/Reset the panic code to arm/disarm your alarm. This is a special code which disarms the alarm when used yet sets a panic state within HA. It can therefore be used in conjunction with automations for example notifiying the authorities or someone who is able to help. To the intruder the alarm appears to have disarmed as normal.</span>
  704.                         <div class='setting-input horizontal layout'>
  705.                           <paper-input id='panic_code' type="password" placeholder='Panic Code'></paper-input>
  706.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='panic_code' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  707.                         </div>
  708.                       </div>
  709.                     </div>
  710.  
  711.                     <div class='setting-outer vertical layout'>
  712.                       <div class='setting-inner horizontal layout'>
  713.                         <span class='info-header'>Require a code to set the alarm: </span>
  714.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.code_to_arm}}' on-change='settingsSave' data-setting='root' data-attribute='code_to_arm'></paper-toggle-button>
  715.                       </div>
  716.                       <span class='info-detail'>If enabled a valid code is required to arm the alarm.</span>
  717.                     </div>
  718.  
  719.                     <div class='setting-outer horizontal layout'>
  720.                       <div class='setting-inner vertical layout'>
  721.                         <span class='info-header'>Passcode Attempts: </span>
  722.                         <span class='info-detail'>Amount of allowed passcode attempts before the panel locks out. Defaults to -1 which means infinite attempts</span>
  723.                         <div class='setting-input horizontal layout'>
  724.                           <paper-input id='passcode_attempts' value='{{alarm.attributes.passcode_attempts}}' placeholder='Passcode Attempts'></paper-input>
  725.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='passcode_attempts' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  726.                         </div>
  727.                       </div>
  728.                     </div>
  729.  
  730.                     <div class='setting-outer horizontal layout'>
  731.                       <div class='setting-inner vertical layout'>
  732.                         <span class='info-header'>Passcode Attempts Timeout: </span>
  733.                         <span class='info-detail'>Amount of time in seconds after the panel is locked before a user is allowed to try again. Defaults to 15 minutes.</span>
  734.                         <div class='setting-input horizontal layout'>
  735.                           <paper-input id='passcode_attempts_timeout' value='{{alarm.attributes.passcode_attempts_timeout}}' placeholder='Passcode Attempts Timeout'></paper-input>
  736.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='passcode_attempts_timeout' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  737.                         </div>
  738.                       </div>
  739.                     </div>
  740.  
  741.                     <div class='setting-outer vertical layout'>
  742.                       <div class='setting-inner horizontal layout'>
  743.                         <span class='info-header'>Enable Log: </span>
  744.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.enable_log}}' on-change='settingsSave' data-setting='root' data-attribute='enable_log'></paper-toggle-button>
  745.                       </div>
  746.                       <span class='info-detail'>When enabled the alarm records a log, permissions must be granted to HA to allow the log file to be saved in the configuration directory as alarm_log.json. Default Disabled.</span>
  747.                     </div>
  748.  
  749.                     <div class='setting-outer horizontal layout'>
  750.                       <div class='setting-inner vertical layout'>
  751.                         <span class='info-header'>log Size: </span>
  752.                         <span class='info-detail'>Amount of records which can be saved and displayed. Defaults to 10 records.</span>
  753.                         <div class='setting-input horizontal layout'>
  754.                           <paper-input id='log_size' value='{{alarm.attributes.log_size}}' placeholder='Log Size'></paper-input>
  755.                           <paper-icon-button data-type='input_text' data-setting='root' data-attribute='log_size' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  756.                         </div>
  757.                       </div>
  758.                     </div>
  759.  
  760.                     <!-- ########################################################################### @USERS ##################################################################################################### -->
  761.                     <h1>User Specific Codes</h1>
  762.  
  763.                     <div class='setting-outer vertical layout'>
  764.                       <span class='info-detail'>This setting provides the ability to configure user specific accounts that are able to control your alarm. If logging is enabled then these users will be captured as part of the log. Home Assistant users using the new authentication system introcuded in v0.70 will be automatically imported.</span>
  765.                     </div>
  766.  
  767.                     <template is='dom-repeat' items='[[alarm.attributes.users]]' as='user'>
  768.  
  769.                       <div class='setting-outer vertical layout'>
  770.                         <div class='setting-inner horizontal layout'>
  771.  
  772.                           <div style='margin-top: auto; margin-bottom: auto; margin-left: 15px;'>
  773.                             <paper-toggle-button class='setting-toggle' checked='{{user.enabled}}' data-userid$='[[user.id]]' on-change='toggleUser'>Enable</paper-toggle-button>
  774.                           </div>
  775.                           <paper-button data-type='button' style='background-color: unset; width: 40%; justify-content: left;' data-userid$='{{user.id}}' on-click='editUser'>
  776.                             <div style='margin-left: 5px; margin-right: 10px; padding-left: 15px;' data-userid$='[[user.id]]' on-click='editUser'>
  777.                               <state-badge style$='background-image: url({{computeImage(user.picture)}});' data-userid$='[[user.id]]' on-click='editUser'></state-badge>
  778.                             </div>
  779.                             <div style='margin-top: auto; margin-bottom: auto; font-size: x-large;' data-userid$='[[user.id]]' on-click='editUser'>
  780.                               [[user.name]]
  781.                             </div>
  782.                           </paper-button>
  783.  
  784.                           <div style='margin-top: auto; margin-bottom: auto;'>
  785.                             <paper-button  data-userid$='[[user.id]]' on-click='deleteUser' style='color: white;background-color: unset;'><iron-icon icon='mdi:delete'></iron-icon>&nbsp;&nbsp;Delete</paper-button>
  786.                           </div>
  787.                         </div>
  788.                       </div>
  789.                     </template>
  790.  
  791.                     <paper-button id='user-add' data-type='button' on-click='unhideDiv' data-div='#userDetail' style='background-color: unset; margin-bottom: 15px;'><iron-icon icon='mdi:account-plus'></iron-icon>&nbsp;&nbsp;Add A New User</paper-button>
  792.  
  793.                     <div id='userDetail' class='remove'>
  794.  
  795.                       <h1>Update User</h1>
  796.  
  797.                       <div class='setting-outer horizontal layout'>
  798.                         <div class='setting-inner vertical layout'>
  799.                           <span class='info-header'>Name: </span>
  800.                           <span class='info-detail'>Set a name for your user.</span>
  801.                           <div class='setting-input horizontal layout'>
  802.                             <paper-input id='user-name' placeholder='Name of user' required auto-validate error-message="Required." ></paper-input>
  803.                           </div>
  804.                         </div>
  805.                       </div>
  806.  
  807.                       <div class='setting-outer vertical layout'>
  808.                         <div class='setting-inner horizontal layout'>
  809.                           <span class='info-header'>Enable User: </span>
  810.                           <paper-toggle-button id='user-enabled' checked='true'></paper-toggle-button>
  811.                         </div>
  812.                         <span class='info-detail'>Enable this user to control the alarm?</span>
  813.                       </div>
  814.  
  815.                       <div class='setting-outer horizontal layout'>
  816.                         <div class='setting-inner vertical layout'>
  817.                           <span class='info-header'>Passcode: </span>
  818.                           <span class='info-detail'>Set a new passcode for your user.</span>
  819.                           <div class='setting-input horizontal layout'>
  820.                             <paper-input id='user-passcode' placeholder='User passcode' type="password" required auto-validate error-message="Required - Minimum of 4 digits/characters" char-counter minlength='4'></paper-input>
  821.                           </div>
  822.                         </div>
  823.                       </div>
  824.  
  825.                       <div class='setting-outer horizontal layout'>
  826.                         <div class='setting-inner vertical layout'>
  827.  
  828.                           <span class='info-header'>Badge: </span>
  829.                           <span class='info-detail'>Enter the location and name of your image. Ensure you upload your image to your HA server under the folders [HA config]/www/images. then use filename /local/images/myimage.jpg.</span>
  830.                           <div class='setting-input horizontal layout'>
  831.                             <state-badge id='user-badge' style='background-image: url("/local/images/ha.png"); margin-top: auto; margin-bottom: auto; margin-right: 15px;'></state-badge>
  832.                             <paper-input id='user-picture' placeholder='Image path i.e. /local/images/myimage.jpg' on-change="loadPreviewImage" ></paper-input>
  833.                           </div>
  834.                         </div>
  835.                       </div>
  836.  
  837.                       <!-- <div class='setting-outer vertical layout'>
  838.                         <div class='setting-inner horizontal layout'>
  839.                           <span class='info-header'>Disable Animations: </span>
  840.                           <paper-toggle-button id='user-disable_animations' checked='false'></paper-toggle-button>
  841.                         </div>
  842.                         <span class='info-detail'>If enabled, the animations in the panel will be disabled for this user to help with performance on older devices.</span>
  843.                       </div> -->
  844.  
  845.                       <!-- <h1>Permissions Based Settings</h1>
  846.                       <span class='info-detail'>Customise which areas of the alarm you want to allow your user to control. By default the user can control all areas.</span>
  847.  
  848.                       <div class='setting-outer vertical layout'>
  849.                         <div class='setting-inner horizontal layout'>
  850.                           <span class='info-header'>Home: </span>
  851.                           <paper-toggle-button id='user-home-permission' checked='true' class='setting-toggle'></paper-toggle-button>
  852.                         </div>
  853.                       </div>
  854.  
  855.                       <div class='setting-outer vertical layout'>
  856.                         <div class='setting-inner horizontal layout'>
  857.                           <span class='info-header'>Away: </span>
  858.                           <paper-toggle-button id='user-away-permission' checked='true' class='setting-toggle'></paper-toggle-button>
  859.                         </div>
  860.                       </div>
  861.  
  862.                       <div class='setting-outer vertical layout'>
  863.                         <div class='setting-inner horizontal layout'>
  864.                           <span class='info-header'>Perimeter: </span>
  865.                           <paper-toggle-button id='user-perimeter-permission' checked='true' class='setting-toggle'></paper-toggle-button>
  866.                         </div>
  867.                       </div> -->
  868.  
  869.                       <paper-button id='user-save' data-type='button' on-click='saveUser' style='background-color: unset; margin-bottom: 15px;'><iron-icon icon='mdi:content-save'></iron-icon>&nbsp;&nbsp;Save User</paper-button>
  870.                       <paper-button on-click='resetUserDetail' style='background-color: unset; margin-bottom: 15px;'><iron-icon icon='mdi:cancel'></iron-icon>&nbsp;&nbsp;Cancel</paper-button>
  871.                     </div>
  872.  
  873.                   </div>
  874.  
  875.                   <!-- ########################################################################### @SENSORS ##################################################################################################### -->
  876.                   <div id='settings-sensors' class='carousel-cell remove'>
  877.                     <h1>Sensors</h1>
  878.  
  879.                     <div class='setting-outer vertical layout'>
  880.                       <div class='setting-inner horizontal layout'>
  881.                         <span class='info-header'>Enable Sensors Panel: </span>
  882.                         <paper-toggle-button class='setting-toggle' checked='{{isChecked(alarm.attributes.panel.enable_sensors_panel)}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_sensors_panel'></paper-toggle-button>
  883.                       </div>
  884.                       <span class='info-detail'>Enabling this allows you to view your sensors groups in a panel</span>
  885.                     </div>
  886.  
  887.                     <h1>Away Mode</h1>
  888.  
  889.                     <div class='setting-outer horizontal layout'>
  890.                       <div class='setting-inner vertical layout'>
  891.                         <span class='info-header'>Pending (Exit) Time: </span>
  892.                         <span class='info-detail'>Grace time in seconds before the alarm is armed. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Exit Time'. Default is 25 seconds</span>
  893.                         <div class='setting-input horizontal layout'>
  894.                           <paper-input id='armed_away-pending_time' value='[[alarm.attributes.states.armed_away.pending_time]]' placeholder='Pending Time'></paper-input>
  895.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_away' data-attribute='pending_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  896.                         </div>
  897.                       </div>
  898.                     </div>
  899.  
  900.                     <div class='setting-outer horizontal layout'>
  901.                       <div class='setting-inner vertical layout'>
  902.                         <span class='info-header'>Warning (Entry) Time: </span>
  903.                         <span class='info-detail'>Grace time in seconds before the alarm is triggered. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Entry Time'. Default is 25 seconds</span>
  904.                         <div class='setting-input horizontal layout'>
  905.                           <paper-input id='armed_away-warning_time' value='[[alarm.attributes.states.armed_away.warning_time]]' placeholder='Warning Time'></paper-input>
  906.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_away' data-attribute='warning_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  907.                         </div>
  908.                       </div>
  909.                     </div>
  910.  
  911.                     <div class='setting-outer horizontal layout'>
  912.                       <div class='setting-inner vertical layout'>
  913.                         <span class='info-header'>Trigger Time: </span>
  914.                         <span class='info-detail'>The amount of time in seconds the alarm stays triggered before returning to its previous state. Default is 300 seconds</span>
  915.                         <div class='setting-input horizontal layout'>
  916.                           <paper-input id='armed_away-trigger_time' value='[[alarm.attributes.states.armed_away.trigger_time]]' placeholder='Trigger Time'></paper-input>
  917.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_away' data-attribute='trigger_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  918.                         </div>
  919.                       </div>
  920.  
  921.                     </div>
  922.  
  923.                     <div class='horizontal layout' id='sensors-away-immediate'>
  924.  
  925.                       <div class='vertical layout'>
  926.                         <h2>Inmediatos</h2>
  927.                         <paper-listbox id='sensors-checkboxs' class='vertical layout' attr-for-selected='title' multi selected-values='{{alarm.attributes.states.armed_away.immediate}}' selected-attribute='checked' >
  928.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  929.                             <checkbox type='checkbox' class='checkbox' title="[[sensor]]" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_away' data-group='immediate' >[[computeFriendlyName(sensor)]]</checkbox>
  930.                           </template>
  931.                         </paper-listbox>
  932.                       </div>
  933.  
  934.                       <div class='sensor vertical layout'>
  935.                         <h2>Retrasados</h2>
  936.                         <paper-listbox id='sensors-checkboxs1' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_away.delayed}} selected-attribute="checked" >
  937.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  938.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_away' data-group='delayed' >[[computeFriendlyName(sensor)]]</checkbox>
  939.                           </template>
  940.                         </paper-listbox>
  941.                       </div>
  942.  
  943.                       <div class='vertical layout'>
  944.                         <h2>Descartar</h2>
  945.                         <paper-listbox id='sensors-checkboxs2' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_away.override}} selected-attribute="checked" >
  946.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  947.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_away' data-group='override' >[[computeFriendlyName(sensor)]]</checkbox>
  948.                           </template>
  949.                         </paper-listbox>
  950.                       </div>
  951.                     </div>
  952.  
  953.                     <h1>Home Mode</h1>
  954.  
  955.                     <div class='setting-outer horizontal layout'>
  956.                       <div class='setting-inner vertical layout'>
  957.                         <span class='info-header'>Pending (Exit) Time: </span>
  958.                         <span class='info-detail'>Grace time in seconds before the alarm is armed. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Exit Time'. Default is 25 seconds</span>
  959.                         <div class='setting-input horizontal layout'>
  960.                           <paper-input id='armed_home-pending_time' value='[[alarm.attributes.states.armed_home.pending_time]]' placeholder='Pending Time'></paper-input>
  961.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_home' data-attribute='pending_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  962.                         </div>
  963.                       </div>
  964.  
  965.                     </div>
  966.  
  967.                     <div class='setting-outer horizontal layout'>
  968.                       <div class='setting-inner vertical layout'>
  969.                         <span class='info-header'>Warning (Entry) Time: </span>
  970.                         <span class='info-detail'>Grace time in seconds before the alarm is triggered. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Entry Time'. Default is 25 seconds</span>
  971.                         <div class='setting-input horizontal layout'>
  972.                           <paper-input id='armed_home-warning_time' value='[[alarm.attributes.states.armed_home.warning_time]]' placeholder='Warning Time'></paper-input>
  973.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_home' data-attribute='warning_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  974.                         </div>
  975.                       </div>
  976.                     </div>
  977.  
  978.                     <div class='setting-outer horizontal layout'>
  979.                       <div class='setting-inner vertical layout'>
  980.                         <span class='info-header'>Trigger Time: </span>
  981.                         <span class='info-detail'>The amount of time in seconds the alarm stays triggered before returning to its previous state. Default is 300 seconds</span>
  982.                         <div class='setting-input horizontal layout'>
  983.                           <paper-input id='armed_home-trigger_time' value='[[alarm.attributes.states.armed_home.trigger_time]]' placeholder='Trigger Time'></paper-input>
  984.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_home' data-attribute='trigger_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  985.                         </div>
  986.                       </div>
  987.                     </div>
  988.  
  989.                     <div class='horizontal layout'>
  990.                       <div class='vertical layout'>
  991.                         <h2>Immediate</h2>
  992.                         <paper-listbox id='sensors-checkboxs3' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_home.immediate}} selected-attribute="checked" >
  993.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  994.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_home' data-group='immediate' >[[computeFriendlyName(sensor)]]</checkbox>
  995.                           </template>
  996.                         </paper-listbox>
  997.                       </div>
  998.  
  999.                       <div class='vertical layout'>
  1000.  
  1001.                         <h2>Delayed</h2>
  1002.                         <paper-listbox id='sensors-checkboxs4' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_home.delayed}} selected-attribute="checked" >
  1003.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  1004.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_home' data-group='delayed' >[[computeFriendlyName(sensor)]]</checkbox>
  1005.                           </template>
  1006.                         </paper-listbox>
  1007.                       </div>
  1008.  
  1009.                       <div class='vertical layout'>
  1010.  
  1011.                         <h2>Override</h2>
  1012.                         <paper-listbox id='sensors-checkboxs5' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_home.override}} selected-attribute="checked" >
  1013.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  1014.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_home' data-group='override' >[[computeFriendlyName(sensor)]]</checkbox>
  1015.                           </template>
  1016.                         </paper-listbox>
  1017.                       </div>
  1018.                     </div>
  1019.  
  1020.                     <h1>Modo Perimetro</h1>
  1021.  
  1022.                     <div class='setting-outer vertical layout'>
  1023.                       <div class='setting-inner horizontal layout'>
  1024.                         <span class='info-header'>Enable Perimeter Mode: </span>
  1025.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.enable_perimeter_mode}}' on-change='settingsSave' data-setting='root' data-attribute='enable_perimeter_mode'></paper-toggle-button>
  1026.                       </div>
  1027.                       <span class='info-detail'>Enable perimeter mode. Default is False</span>
  1028.                     </div>
  1029.  
  1030.                     <div class='setting-outer horizontal layout'>
  1031.                       <div class='setting-inner vertical layout'>
  1032.                         <span class='info-header'>Pending (Exit) Time: </span>
  1033.                         <span class='info-detail'>Grace time in seconds before the alarm is armed. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Exit Time'. Default is 25 seconds</span>
  1034.                         <div class='setting-input horizontal layout'>
  1035.                           <paper-input id='armed_perimeter-pending_time' value='[[alarm.attributes.states.armed_perimeter.pending_time]]' placeholder='Pending Time'></paper-input>
  1036.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_perimeter' data-attribute='pending_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1037.                         </div>
  1038.                       </div>
  1039.                     </div>
  1040.  
  1041.                     <div class='setting-outer horizontal layout'>
  1042.                       <div class='setting-inner vertical layout'>
  1043.                         <span class='info-header'>Warning (Entry) Time: </span>
  1044.                         <span class='info-detail'>Grace time in seconds before the alarm is triggered. Any sensor tripped within this time period will not trigger the alarm. Also known as 'Entry Time'. Default is 25 seconds</span>
  1045.                         <div class='setting-input horizontal layout'>
  1046.                           <paper-input id='armed_perimeter-warning_time' value='[[alarm.attributes.states.armed_perimeter.warning_time]]' placeholder='Warning Time'></paper-input>
  1047.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_perimeter' data-attribute='warning_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1048.                         </div>
  1049.                       </div>
  1050.                     </div>
  1051.  
  1052.                     <div class='setting-outer horizontal layout'>
  1053.                       <div class='setting-inner vertical layout'>
  1054.                         <span class='info-header'>Trigger Time: </span>
  1055.                         <span class='info-detail'>The amount of time in seconds the alarm stays triggered before returning to its previous state. Default is 300 seconds</span>
  1056.                         <div class='setting-input horizontal layout'>
  1057.                           <paper-input id='armed_perimeter-trigger_time' value='[[alarm.attributes.states.armed_perimeter.trigger_time]]' placeholder='Trigger Time'></paper-input>
  1058.                           <paper-icon-button data-type='input_text' data-setting='states' data-mode='armed_perimeter' data-attribute='trigger_time' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1059.                         </div>
  1060.                       </div>
  1061.                     </div>
  1062.  
  1063.                     <div class='horizontal layout'>
  1064.                       <div class='vertical layout'>
  1065.                         <h2>Immediate</h2>
  1066.                         <paper-listbox id='sensors-checkboxs6' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_perimeter.immediate}} selected-attribute="checked" >
  1067.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  1068.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_perimeter' data-group='immediate' >[[computeFriendlyName(sensor)]]</checkbox>
  1069.                           </template>
  1070.                         </paper-listbox>
  1071.                       </div>
  1072.  
  1073.                       <div class='vertical layout'>
  1074.  
  1075.                         <h2>Delayed</h2>
  1076.                         <paper-listbox id='sensors-checkboxs7' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_perimeter.delayed}} selected-attribute="checked" >
  1077.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  1078.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_perimeter' data-group='delayed' >[[computeFriendlyName(sensor)]]</checkbox>
  1079.                           </template>
  1080.                         </paper-listbox>
  1081.                       </div>
  1082.  
  1083.                       <div class='vertical layout'>
  1084.  
  1085.                         <h2>Override</h2>
  1086.                         <paper-listbox id='sensors-checkboxs8' class='vertical layout' attr-for-selected="title" multi selected-values={{alarm.attributes.states.armed_perimeter.override}} selected-attribute="checked" >
  1087.                           <template is='dom-repeat' items='{{binary_sensors}}' as='sensor'>
  1088.                             <checkbox type='checkbox' class='checkbox' title="{{sensor}}" on-click='settingsSave' data-setting='sensor_select' data-mode='armed_perimeter' data-group='override' >[[computeFriendlyName(sensor)]]</checkbox>
  1089.                           </template>
  1090.                         </paper-listbox>
  1091.                       </div>
  1092.                     </div>
  1093.  
  1094.                   </div>
  1095.  
  1096.                   <!-- ########################################################################### @FLOORPLAN ##################################################################################################### -->
  1097.                   <div id='settings-floorplan' class='carousel-cell remove'>
  1098.                     <h1>Floorplan</h1>
  1099.  
  1100.                     <div class='setting-outer vertical layout'>
  1101.                       <div class='setting-inner horizontal layout'>
  1102.                         <span class='info-header'>Enable Floorplan Panel: </span>
  1103.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.panel.enable_floorplan_panel}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_floorplan_panel'></paper-toggle-button>
  1104.                       </div>
  1105.                       <span class='info-detail'>Enabling this allows you to setup your custom floorplan to be displayed as a panel</span>
  1106.                     </div>
  1107.  
  1108.                     <div class='setting-outer vertical layout'>
  1109.                       <div class='horizontal layout'>
  1110.                         <div class='setting-inner vertical layout'>
  1111.                           <span class='info-header'>Floorplan Entity: </span>
  1112.                           <span class='info-detail'>Choose the binary_sensor which relates to your floorplan setup</span>
  1113.                         </div>
  1114.                       </div>
  1115.                       <div class='setting-inner vertical layout'>
  1116.                         <paper-listbox id='floorplan-listbox' class='vertical layout' selected='{{alarm.attributes.panel.floorplan}}' attr-for-selected="name" on-selected-item-changed='settingsSave' data-type='listbox' data-setting='panel' data-attribute='floorplan'>
  1117.                           <template is='dom-repeat' items='{{binary_sensors}}' as='bs'>
  1118.                             <paper-item name="[[bs]]" class='list-item' title="{{bs}}">[[computeFriendlyName(bs)]]</paper-item>
  1119.                           </template>
  1120.                         </paper-listbox>
  1121.                       </div>
  1122.                     </div>
  1123.                   </div>
  1124.                   <!-- ########################################################################### CAMERAS ##################################################################################################### -->
  1125.                   <div id='settings-cameras' class='carousel-cell remove'>
  1126.                     <h1>Cameras</h1>
  1127.  
  1128.                     <div class='setting-outer vertical layout'>
  1129.                       <div class='setting-inner horizontal layout'>
  1130.                         <span class='info-header'>Enable Camera Panel: </span>
  1131.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.panel.enable_camera_panel}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_camera_panel'></paper-toggle-button>
  1132.                       </div>
  1133.                       <span class='info-detail'>Enabling this allows you to add your cameras listed below to be displayed as a panel</span>
  1134.                     </div>
  1135.  
  1136.                     <div class='setting-outer horizontal layout'>
  1137.                       <div class='setting-inner vertical layout'>
  1138.                         <span class='info-header'>Update Interval: </span>
  1139.                         <span class='info-detail'>The time in seconds the camera image updates. Default is 5 seconds</span>
  1140.                         <div class='setting-input horizontal layout'>
  1141.                           <paper-input id='camera_update_interval' value='{{alarm.attributes.panel.camera_update_interval}}' placeholder='Update Interval'></paper-input>
  1142.                           <paper-icon-button data-type='input_text' data-setting='panel' data-attribute='camera_update_interval' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1143.                         </div>
  1144.                       </div>
  1145.                     </div>
  1146.  
  1147.                     <paper-listbox id='cameras-checkboxs' class='vertical layout' attr-for-selected="name" multi selected-values={{alarm.attributes.panel.cameras}} selected-attribute="checked" on-selected-items-changed='settingsSave' data-type='checkbox' data-setting='panel-cameras'>
  1148.                       <template is='dom-repeat' items='{{cameras}}' as='camera'>
  1149.                         <checkbox name="{{camera}}" class='checkbox' title$="{{camera}}">[[computeFriendlyName(camera)]]</checkbox>
  1150.                       </template>
  1151.                     </paper-listbox>
  1152.                   </div>
  1153.  
  1154.                   <!-- ########################################################################### MQTT ##################################################################################################### -->
  1155.                   <div id='settings-mqtt' class='carousel-cell remove'>
  1156.                     <h1>MQTT</h1>
  1157.  
  1158.                     <div class='setting-outer vertical layout'>
  1159.                       <div class='setting-inner horizontal layout'>
  1160.                         <span class='info-header'>Enable MQTT:</span>
  1161.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.mqtt.enable_mqtt}}' on-change='settingsSave' data-setting='mqtt' data-attribute='enable_mqtt'></paper-toggle-button>
  1162.                       </div>
  1163.                       <span class='info-detail'>(RESTART REQUIRED) Enabling this allows you to send and receive MQTT messages to interact with your alarm</span>
  1164.                     </div>
  1165.  
  1166.                     <div class='setting-outer horizontal layout'>
  1167.                       <div class='setting-inner vertical layout'>
  1168.                         <span class='info-header'>QOS: </span>
  1169.                         <span class='info-detail'>Quality of Service. The maximum QoS level for subscribing and publishing to MQTT messages. Default is 0.</span>
  1170.                         <div class='setting-input horizontal layout'>
  1171.                           <paper-input id='qos' value='{{alarm.attributes.mqtt.qos}}' placeholder='QOS'></paper-input>
  1172.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='qos' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1173.                         </div>
  1174.                       </div>
  1175.                     </div>
  1176.  
  1177.                     <div class='setting-outer horizontal layout'>
  1178.                       <div class='setting-inner vertical layout'>
  1179.                         <span class='info-header'>State Topic: </span>
  1180.                         <span class='info-detail'>The MQTT topic HA will publish state updates to. Default is 'home/alarm'</span>
  1181.                         <div class='setting-input horizontal layout'>
  1182.                           <paper-input id='state_topic' value='{{alarm.attributes.mqtt.state_topic}}' placeholder='State Topic'></paper-input>
  1183.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='state_topic' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1184.                         </div>
  1185.                       </div>
  1186.                     </div>
  1187.  
  1188.                     <div class='setting-outer horizontal layout'>
  1189.                       <div class='setting-inner vertical layout'>
  1190.                         <span class='info-header'>Command Topic: </span>
  1191.                         <span class='info-detail'>The MQTT topic HA will subscribe to, to receive commands from a remote device to change the alarm state. Default is 'home/alarm/set'</span>
  1192.                         <div class='setting-input horizontal layout'>
  1193.                           <paper-input id='command_topic' value='{{alarm.attributes.mqtt.command_topic}}' placeholder='Command Topic'></paper-input>
  1194.                           <paper-icon-button data-type='imput_text' data-setting='mqtt' data-attribute='command_topic' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1195.                         </div>
  1196.                       </div>
  1197.                     </div>
  1198.  
  1199.                     <div class='setting-outer horizontal layout'>
  1200.                       <div class='setting-inner vertical layout'>
  1201.                         <span class='info-header'>Payload Disarm: </span>
  1202.                         <span class='info-detail'>The payload to disarm this Alarm Panel. Default is 'DISARM'.</span>
  1203.                         <div class='setting-input horizontal layout'>
  1204.                           <paper-input id='payload_disarm' value='{{alarm.attributes.mqtt.payload_disarm}}' placeholder='Payload Disarm'></paper-input>
  1205.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='payload_disarm' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1206.                         </div>
  1207.                       </div>
  1208.                     </div>
  1209.  
  1210.                     <div class='setting-outer horizontal layout'>
  1211.                       <div class='setting-inner vertical layout'>
  1212.                         <span class='info-header'>Payload Arm Home: </span>
  1213.                         <span class='info-detail'>The payload to set armed-home mode on this Alarm Panel. Default is 'ARM_HOME'.</span>
  1214.                         <div class='setting-input horizontal layout'>
  1215.                           <paper-input id='payload_arm_home' value='{{alarm.attributes.mqtt.payload_arm_home}}' placeholder='Payload Disarm'></paper-input>
  1216.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='payload_arm_home' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1217.                         </div>
  1218.                       </div>
  1219.                     </div>
  1220.  
  1221.                     <div class='setting-outer horizontal layout'>
  1222.                       <div class='setting-inner vertical layout'>
  1223.                         <span class='info-header'>Payload Arm Away: </span>
  1224.                         <span class='info-detail'>The payload to set armed-away mode on this Alarm Panel. Default is 'ARM_AWAY'.</span>
  1225.                         <div class='setting-input horizontal layout'>
  1226.                           <paper-input id='payload_arm_away' value='{{alarm.attributes.mqtt.payload_arm_away}}' placeholder='Payload Away'></paper-input>
  1227.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='payload_arm_away' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1228.                         </div>
  1229.                       </div>
  1230.                     </div>
  1231.  
  1232.                     <div class='setting-outer horizontal layout'>
  1233.                       <div class='setting-inner vertical layout'>
  1234.                         <span class='info-header'>Payload Arm Perimeter: </span>
  1235.                         <span class='info-detail'>The payload to set armed-perimeter mode on this Alarm Panel. Default is 'ARM_NIGHT'.</span>
  1236.                         <div class='setting-input horizontal layout'>
  1237.                           <paper-input id='payload_arm_night' value='{{alarm.attributes.mqtt.payload_arm_night}}' placeholder='Payload Perimeter'></paper-input>
  1238.                           <paper-icon-button data-type='input_text' data-setting='mqtt' data-attribute='payload_arm_night' on-click='settingsSave' icon='mdi:check-circle-outline'></paper-icon-button>
  1239.                         </div>
  1240.                       </div>
  1241.                     </div>
  1242.  
  1243.                     <div class='setting-outer vertical layout'>
  1244.                       <div class='setting-input horizontal layout'>
  1245.                         <span class='info-header'>Override Code:</span>
  1246.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.mqtt.override_code}}' on-change='settingsSave' data-setting='mqtt' data-attribute='override_code'></paper-toggle-button>
  1247.                       </div>
  1248.                       <span class='info-detail'>If true allows MQTT commands to disarm the alarm without a valid code. False by default.</span>
  1249.                     </div>
  1250.  
  1251.                     <div class='setting-outer vertical layout'>
  1252.                       <div class='setting-inner horizontal layout'>
  1253.                         <span class='info-header'>Pending On Warning:</span>
  1254.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.mqtt.pending_on_warning}}' on-change='settingsSave' data-setting='mqtt' data-attribute='pending_on_warning'></paper-toggle-button>
  1255.                       </div>
  1256.                       <span class='info-detail'>Broadcast Pending state when the alarm is tripped instead of the default Warning state. This is to allow integration with other MQTT panels which use this state. False by default.</span>
  1257.                     </div>
  1258.  
  1259.                   </div>
  1260.  
  1261.                   <!-- ########################################################################### CUSTOM ##################################################################################################### -->
  1262.                   <div id='settings-custom' class='carousel-cell remove'>
  1263.                     <h1>Custom</h1>
  1264.                     <div class='setting-outer vertical layout'>
  1265.                       <div class='setting-inner horizontal layout'>
  1266.                         <span class='info-header'>Enable Custom Panel:</span>
  1267.                         <paper-toggle-button class='setting-toggle' checked='{{alarm.attributes.panel.enable_custom_panel}}' on-change='settingsSave' data-setting='panel' data-attribute='enable_custom_panel'></paper-toggle-button>
  1268.                       </div>
  1269.                       <span class='info-detail'>Enabling this allows you to add your own panel to this interface. For this to work see the template custom-element.html file. You will need to modify this with your own HTML code. Using this you could bring additional custom features of your own such as displaying extra information from Home Assistant or a 3rd party website. You could hook into external cameras or have a stocks and shared ticker.</span>
  1270.                     </div>
  1271.                   </div>
  1272.  
  1273.                   <!-- ########################################################################### HELP ##################################################################################################### -->
  1274.                   <!-- <div id='settings-help' class='carousel-cell remove'>
  1275.                     <h1>Help Guide</h1>
  1276.                     <div class='setting-outer vertical layout'>
  1277.                       <span class='info-detail'>This section is a work in progress. I'm knackered from just creating this interface, too many late nights :-D Let me know what you think would be good to display here. I'll likely provide the basics plus links to yaml automations and sample node-red files</span>
  1278.                     </div>
  1279.                   </div> -->
  1280.  
  1281.                 </div>
  1282.               </div>
  1283.             </div>
  1284.           </div>
  1285.         </div>
  1286.      </div>
  1287.       <!-- ###########################################SELECTION BAR############################################### -->
  1288.       <div id='info-panel-selection'>
  1289.        <paper-icon-button icon='mdi:dialpad' title='Keypad' on-click='carouselClick' data-selection='controls'></paper-icon-button>
  1290.        <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_floorplan_panel)]]' >
  1291.          <paper-icon-button icon='mdi:floor-plan' title='Floorplan' on-click='carouselClick' data-selection='floorplan'></paper-icon-button>
  1292.         </template>
  1293.        <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_sensors_panel)]]' >
  1294.           <paper-icon-button icon='mdi:radio-tower' title='Alarm Sensors' on-click='carouselClick' data-selection='sensors' style$='color: [[computeButtonColor(opencount)]] !important;'></paper-icon-button>
  1295.        </template>
  1296.        <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_camera_panel)]]' >
  1297.          <paper-icon-button icon='mdi:cctv' title='Cameras' on-click='carouselClick' data-selection='cameras'></paper-icon-button>
  1298.         </template>
  1299.        <template is='dom-if' if='[[isChecked(alarm.attributes.enable_log)]]' >
  1300.          <paper-icon-button icon='mdi:view-headline' title='Activity Log' on-click='carouselClick' data-selection='log'></paper-icon-button>
  1301.         </template>
  1302.        <template is='dom-if' if='[[isChecked(alarm.attributes.panel.enable_custom_panel)]]' >
  1303.          <paper-icon-button icon='mdi:atom' title='Custom Panel' on-click='carouselClick' data-selection='custom'></paper-icon-button>
  1304.         </template>
  1305.        <paper-icon-button icon='mdi:tune' title='Settings' on-click='carouselClick' data-selection='set-login'></paper-icon-button>
  1306.      </div>
  1307.       <!-- ###########################################CAMERA MODAL############################################### -->
  1308.       <div id="modalCamera">
  1309.        <img id="imgCamera" >
  1310.        <div id='captionCamera'></div>
  1311.       </div>
  1312.       <!-- ###########################################DONATE MODAL############################################### -->
  1313.       <div id='modalDonate'>
  1314.        <div id='donateMethod' class='info-header'></div>
  1315.         <div class='logo' style='padding-bottom: 20px;'></div>
  1316.        <div class='qr'></div>
  1317.        <div id='donateAddress' class='info-detail'></div>
  1318.      </div>
  1319.       <!-- ###########################################ERROR MODAL############################################### -->
  1320.       <template is='dom-if' if='[[items]]' >
  1321.         <div id='modalError' style='color: white'>
  1322.          <div class='horizontal layout'>
  1323.            <div id='error-icon'>
  1324.              <iron-icon icon='mdi:alert-circle-outline'></iron-icon>
  1325.            </div>
  1326.            <div>
  1327.              <div id='error-title'>Whoops!</div>
  1328.              <div id='error-header'>Something went wrong with your alarm component!</div>
  1329.              <div id='error-detail'>The following errors have been plucked from the Home Assistant log:</div>
  1330.              <template is='dom-repeat' items='[[items]]'>
  1331.               <paper-item on-click='openLog'>
  1332.                 <paper-item-body two-line>
  1333.                   <div class="row">
  1334.                     [[item.message]]
  1335.                    </div>
  1336.                   <div secondary>
  1337.                     [[formatTime(item.timestamp)]] [[item.source]] ([[item.level]])
  1338.                    </div>
  1339.                 </paper-item-body>
  1340.               </paper-item>
  1341.              </template>
  1342.               <div id='error-version'>
  1343.                 <p><span class='info-detail'>Home Assistant: </span><span class='info-header'>v[[hass.config.version]]</span></p>
  1344.                 <p><span class='info-detail'>This Panel: </span><span class='info-header'>v[[version]]</span></p>
  1345.            </div>
  1346.            </div>
  1347.          </div>
  1348.        </div>
  1349.      </template>
  1350.    </ha-app-layout>
  1351.  </template>
  1352. </dom-module>
  1353. <script>
  1354.  {
  1355.     class HaPanelAlarm extends Polymer.Element {
  1356.  
  1357.       static get is(){ return 'ha-panel-alarm'; }
  1358.  
  1359.       static get properties(){
  1360.         return {
  1361.           hass:                { type: Object },
  1362.           panel:               { type: Object },
  1363.           narrow:              { type: Boolean, value: false },
  1364.           showMenu:            { type: Boolean, value: false },
  1365.  
  1366.           settingsUnlock:      { type: Boolean, value: false },
  1367.  
  1368.           panel_title:         { type: String, value: 'Home Alarm'},
  1369.  
  1370.           carouselMainSelected: { type: Object },
  1371.           carouselSettings:    { type: Object },
  1372.           controls:            { type: Object },
  1373.           controlsLoaded:      { type: Boolean, value: false },
  1374.  
  1375.           alarm:               { type: Object, observer: 'monitorAlarm' },
  1376.  
  1377.           attemptArmMode:      { type: String, value: ''},
  1378.  
  1379.           // Sensor Groups
  1380.           immediate:           { type: Array, computed: 'computeSensors(hass, alarm.attributes.immediate)' },
  1381.           delayed:             { type: Array, computed: 'computeSensors(hass, alarm.attributes.delayed)' },
  1382.           allsensors:          { type: Array, computed: 'computeSensors(hass, alarm.attributes.allsensors)'},
  1383.           ignored:             { type: Array, computed: 'computeSensors(hass, alarm.attributes.ignored)' },
  1384.  
  1385.           opencount:           { type: Number, value: 0 },
  1386.  
  1387.           time:                { type: Object },
  1388.           weather:             { type: Object },
  1389.           temp:                { type: String },
  1390.  
  1391.           code:                { type: String, value: '' },
  1392.           display_code:        { type: String, value: '' },
  1393.  
  1394.           timeoutID:           { type: Number },
  1395.  
  1396.           sidebarTimerId:      { type: Number },
  1397.  
  1398.           cleanup:             { type: Array, value: [] },
  1399.           attemptedArm:        { type: Boolean, value: false },
  1400.  
  1401.           settings:            { type: Boolean, value: false },
  1402.  
  1403.           panel_locked:        { type: Boolean, value: false },
  1404.  
  1405.           cameraFeedSrc:       { type: String },
  1406.           camera_time:    { type: String },
  1407.           cameras:        { type: Array, value: [] },
  1408.  
  1409.           binary_sensors: { type: Array, value: [] },
  1410.  
  1411.           errors:         { type: Array },
  1412.           version:        { type: String, value: '1.3.4'},
  1413.  
  1414.           camera_update_interval: { type: Number, value: 5000 }, // ms
  1415.  
  1416.           settingColors: { type: Array, value: [] },
  1417.           selectedUser: { type: Object, value: null},
  1418.  
  1419.         }
  1420.       }
  1421.  
  1422.       // Polymer observers definition
  1423.       static get observers() {
  1424.         return [
  1425.           'onPanelUpdate(hass, panel)',
  1426.           'updateTime(hass)'
  1427.         ]
  1428.       }
  1429.  
  1430.       connectedCallback() {
  1431.         super.connectedCallback();
  1432.       }
  1433.  
  1434.       disconnectedCallback() {
  1435.         super.disconnectedCallback();
  1436.         clearInterval(this.camTimer);
  1437.       }
  1438.  
  1439.       //Page ready
  1440.       ready(){
  1441.         super.ready();
  1442.  
  1443.         // Check the alarm component successfully loaded if not then display an error
  1444.         if (this.alarm){
  1445.           //Countdown360
  1446.           var script = document.createElement('script');
  1447.           script.setAttribute('src', '/local/lib/countdown360.js');
  1448.           document.body.appendChild(script);
  1449.  
  1450.           this.loadSettings();
  1451.  
  1452.           this.controls = this.$.controls;
  1453.  
  1454.           if (this.alarm.attributes.hide_sidebar == true)  this.closeSidebar();
  1455.  
  1456.           //Determine screensize and set appropriate controls
  1457.           this.setupUI();
  1458.  
  1459.           this._loadData();
  1460.         }
  1461.         // Display the error
  1462.         else {
  1463.           console.log("Whoops something went wrong and the alarm component cannot be loaded. Check your Home Assistant error log");
  1464.           this.updating = true;
  1465.           this.hass.callApi("get", "error/all").then((items) => {
  1466.             this.errorItems = items;
  1467.             this.updating = false;
  1468.             this.items = [];
  1469.             for (var item in this.errorItems) {
  1470.               if (this.errorItems[item].message.includes("[alarm"))
  1471.                 this.items.push(this.errorItems[item]);
  1472.             }
  1473.           });
  1474.         }
  1475.       }
  1476.  
  1477.       getKeys(obj){
  1478.         if (obj != null) {
  1479.           return Object.keys(obj);
  1480.         }
  1481.         else return false;
  1482.       }
  1483.  
  1484.       defineColors(ev){
  1485.  
  1486.         this.unhideDiv('#themeDetail');
  1487.  
  1488.         var theme = null;
  1489.  
  1490.         if (ev != null){
  1491.           ev.stopPropagation();
  1492.  
  1493.           var themeName =  ev.target.getAttribute('data-theme');
  1494.  
  1495.           if (this.alarm.attributes.themes)
  1496.             for (var x in this.alarm.attributes.themes)
  1497.               if (this.alarm.attributes.themes[x].name == themeName){
  1498.                 theme = this.alarm.attributes.themes[x];
  1499.                 this.shadowRoot.querySelector('#theme-name').value = themeName;
  1500.                 this.shadowRoot.querySelector('#theme-name-button').setAttribute('name', themeName);
  1501.               }
  1502.         }
  1503.  
  1504.         this.settingColors = [{}];
  1505.  
  1506.         this.settingColors[0] = {
  1507.           'name':'warning_color', 'header':'Warning', 'theme':themeName,
  1508.           'description':'If a sensor is tripped when the alarm is armed the panel will display this color in both the top header background and the centre panel background',
  1509.           'color': (theme != null && theme.warning_color != null) ? theme.warning_color : 'black'};
  1510.  
  1511.         this.settingColors[1] = {
  1512.           'name':'pending_color', 'header':'Pending', 'theme':themeName,
  1513.           'description':'When the alarm is arming the panel will display this color in both the top header background and the centre panel background',
  1514.           'color': (theme != null && theme.pending_color != null) ? theme.pending_color  : 'black'};
  1515.  
  1516.         this.settingColors[2] = {
  1517.           'name':'disarmed_color', 'header':'Disarmed', 'theme':themeName,
  1518.           'description':'When the alarm is disarmed the panel will display this color in both the top header background and the centre panel background',
  1519.           'color': (theme != null && theme.disarmed_color != null) ? theme.disarmed_color : 'black'};
  1520.  
  1521.         this.settingColors[3] = {
  1522.           'name':'triggered_color', 'header':'Triggered', 'theme':themeName,
  1523.           'description':'When the alarm has been triggered the panel will display this color in both the top header background and the centre panel background',
  1524.           'color': (theme != null && theme.triggered_color != null) ? theme.triggered_color : 'black'};
  1525.  
  1526.         this.settingColors[4] = {
  1527.           'name':'armed_home_color', 'header':'Armed in \'Home\' Mode', 'theme':themeName,
  1528.           'description':'When the alarm is set in \'Home Mode\' the panel will display this color in both the top header background and the centre panel background',
  1529.           'color': (theme != null && theme.armed_home_color != null) ? theme.armed_home_color : 'black'};
  1530.  
  1531.         this.settingColors[5] = {
  1532.           'name':'armed_away_color', 'header':'Armed in \'Away\' Mode', 'theme':themeName,
  1533.           'description':'When the alarm is set in \'Away Mode\' the panel will display this color in both the top header background and the centre panel background',
  1534.           'color': (theme != null && theme.armed_away_color != null) ? theme.armed_away_color : 'black'};
  1535.  
  1536.         this.settingColors[6] = {
  1537.           'name':'armed_perimeter_color', 'header':'Armed in \'Perimeter\' Mode', 'theme':themeName,
  1538.           'description':'When the alarm is set in \'Perimeter Mode\' the panel will display this color in both the top header background and the centre panel background',
  1539.           'color': (theme != null && theme.armed_perimeter_color != null) ? theme.armed_perimeter_color  : 'black'};
  1540.  
  1541.         this.settingColors[7] = {
  1542.           'name':'panel_background_color', 'header':'Panel Background', 'theme':themeName,
  1543.           'description':'The background color of the main content section.',
  1544.           'color': (theme != null && theme.panel_background_color != null) ? theme.panel_background_color  : 'black'};
  1545.  
  1546.         this.settingColors[8] = {
  1547.           'name':'panel_outer_background_color', 'header':'Panel Outer Background', 'theme':themeName,
  1548.           'description':'The background color of both the status bar and the menu bar.',
  1549.           'color': (theme != null && theme.panel_outer_background_color != null) ? theme.panel_outer_background_color  : 'black'};
  1550.  
  1551.         this.settingColors[9] = {
  1552.           'name':'panel_text_color', 'header':'Panel Text', 'theme':themeName,
  1553.           'description':'The color of the general text within the panel.',
  1554.           'color': (theme != null && theme.panel_text_color != null) ? theme.panel_text_color  : 'black'};
  1555.  
  1556.         this.settingColors[10] = {
  1557.           'name':'header_background_color', 'header':'Header Background', 'theme':themeName,
  1558.           'description':'The background color of very top header bar.',
  1559.           'color': (theme != null && theme.header_background_color != null) ? theme.header_background_color  : 'black'};
  1560.  
  1561.         this.settingColors[11] = {
  1562.           'name':'header_text_color', 'header':'Header Text', 'theme':themeName,
  1563.           'description':'The text color on very top header bar.',
  1564.           'color': (theme != null && theme.header_text_color != null) ? theme.header_text_color  : 'black'};
  1565.  
  1566.         this.settingColors[12] = {
  1567.           'name':'alarmstatus_text_color', 'header':'Alarm Status Text', 'theme':themeName,
  1568.           'description':'The text color of the Alarm Status.',
  1569.           'color': (theme != null && theme.alarmstatus_text_color != null) ? theme.alarmstatus_text_color : 'black'};
  1570.  
  1571.         this.settingColors[13] = {
  1572.           'name':'time_text_color', 'header':'Time Text', 'theme':themeName,
  1573.           'description':'The text color of the Time label.',
  1574.           'color': (theme != null && theme.time_text_color != null) ? theme.time_text_color  : 'black'};
  1575.  
  1576.         this.settingColors[14] = {
  1577.           'name':'weather_text_color', 'header':'Weather Text', 'theme':themeName,
  1578.           'description':'The text color of the Weather label.',
  1579.           'color': (theme != null && theme.weather_text_color != null) ? theme.weather_text_color  : 'black'};
  1580.  
  1581.         this.settingColors[15] = {
  1582.           'name':'weather_image_color', 'header':'Weather Image', 'theme':themeName,
  1583.           'description':'The color of the Weather image.',
  1584.           'color': (theme != null && theme.weather_image_color != null) ? theme.weather_image_color  : 'black'};
  1585.  
  1586.         this.settingColors[16] = {
  1587.           'name':'info_header_text_color', 'header':'Information Header Text', 'theme':themeName,
  1588.           'description':'The color of the Heading within a particular Section.',
  1589.           'color': (theme != null && theme.info_header_text_color != null) ? theme.info_header_text_color  : 'black'};
  1590.  
  1591.         this.settingColors[17] = {
  1592.           'name':'info_detail_text_color', 'header':'Information Detail Text', 'theme':themeName,
  1593.           'description':'The color of the descriptive text within a particular Section.',
  1594.           'color': (theme != null && theme.info_detail_text_color != null) ? theme.info_detail_text_color  : 'black'};
  1595.  
  1596.         this.settingColors[18] = {
  1597.           'name':'title_text_color',   'header':'Title (Content) Text', 'theme':themeName,
  1598.           'description':'The color of the Title text within a particular section.',
  1599.           'color': (theme != null && theme.title_text_color!= null) ? theme.title_text_color  : 'black'};
  1600.  
  1601.         this.settingColors[19] = {
  1602.           'name':'subtitle_text_color', 'header':'Subtitle (Content) Text', 'theme':themeName,
  1603.           'description':'The color of the Subtitle text within a particular section.',
  1604.           'color': (theme != null && theme.subtitle_text_color!= null) ? theme.subtitle_text_color  : 'black'};
  1605.  
  1606.         this.settingColors[20] = {
  1607.           'name':'openSensors_title_color', 'header':'Open Sensors Title', 'theme':themeName,
  1608.           'description':'The color of the Open Sensors Title to draw attention to the open sensor.',
  1609.           'color': (theme != null && theme.opensensors_title_color != null) ? theme.opensensors_title_color  : 'black'};
  1610.  
  1611.         this.settingColors[21] = {
  1612.           'name':'button_background_color', 'header':'Button Background Color', 'theme':themeName,
  1613.           'description':'The background color of the alarm buttons.',
  1614.           'color': (theme != null && theme.button_background_color != null) ? theme.button_background_color  : 'black'};
  1615.  
  1616.         this.settingColors[22] = {
  1617.           'name':'cancel_color',   'header':'Cancel Button Background', 'theme':themeName,
  1618.           'description':'The background color of the cancel button.',
  1619.           'color': (theme != null && theme.cancel_color != null) ? theme.cancel_color  : 'black'};
  1620.  
  1621.         this.settingColors[23] = {
  1622.           'name':'override_color', 'header':'Override Button Background', 'theme':themeName,
  1623.           'description':'The background color of the override button.',
  1624.           'color': (theme != null && theme.override_color != null) ? theme.override_color  : 'black'};
  1625.  
  1626.         this.settingColors[24] = {
  1627.           'name':'info_panel_buttons_color', 'header':'Menu Buttons', 'theme':themeName,
  1628.           'description':'The color of the menu buttons.',
  1629.           'color': (theme != null && theme.info_panel_buttons_color != null) ? theme.info_panel_buttons_color : 'black'};
  1630.  
  1631.         this.settingColors[25] = {
  1632.           'name':'arm_button_border_color', 'header':'Alarm Button Border', 'theme':themeName,
  1633.           'description':'The border color of the alarm buttons.',
  1634.           'color': (theme != null && theme.arm_button_border_color != null) ? theme.arm_button_border_color  : 'black'};
  1635.  
  1636.         this.settingColors[26] = {
  1637.           'name':'arm_button_text_color', 'header':'Alarm Button Text', 'theme':themeName,
  1638.           'description':'The color of the text within the alarm buttons.',
  1639.           'color': (theme != null && theme.arm_button_text_color != null) ? theme.arm_button_text_color  : 'black'};
  1640.  
  1641.         this.settingColors[27] = {
  1642.           'name':'paper_listbox_background_color', 'header':'Listbox background', 'theme':themeName,
  1643.           'description':'The background color of the listboxes.',
  1644.           'color': (theme != null && theme.paper_listbox_background_color != null) ? theme.paper_listbox_background_color  : 'black'};
  1645.  
  1646.         this.settingColors[28] = {
  1647.           'name':'paper_listbox_color', 'header':'Listbox text', 'theme':themeName,
  1648.           'description':'The text color within the listboxes.',
  1649.           'color': (theme != null && theme.paper_listbox_color != null) ? theme.paper_listbox_color : 'black'};
  1650.  
  1651.         this.settingColors[29] = {
  1652.           'name':'paper_item_selected___color', 'header':'Item Selected', 'theme':themeName,
  1653.           'description':'The text color of the item selected within a selection box.',
  1654.           'color': (theme != null && theme.paper_item_selected___color != null) ? theme.paper_item_selected___color  : 'black'};
  1655.  
  1656.         this.settingColors[30] = {
  1657.           'name':'action_button_border_color', 'header':'Action button border', 'theme':themeName,
  1658.           'description':'The color of the border surrounding the action buttons',
  1659.           'color': (theme != null && theme.action_button_border_color != null) ? theme.action_button_border_color  : 'black'};
  1660.  
  1661.         this.addColorPicker();
  1662.       }
  1663.  
  1664.       addColorPicker(){
  1665.         setTimeout(function(instance) {
  1666.           var colorPickers = instance.shadowRoot.querySelectorAll('.colPicker');
  1667.  
  1668.           for (var i = 0; i <= colorPickers.length - 1; i++) {
  1669.             var input = document.createElement('INPUT');
  1670.             input.id = colorPickers[i].id.replace('_span','');
  1671.             var picker = new jscolor(input)
  1672.             picker.fromString(colorPickers[i].value);
  1673.  
  1674.             //clear previous pickers
  1675.             while (colorPickers[i].firstChild)
  1676.               colorPickers[i].removeChild(colorPickers[i].firstChild);
  1677.  
  1678.             //var clearBox = colorPickers[i];
  1679.             if (colorPickers[i] != null)
  1680.               colorPickers[i].appendChild(input);
  1681.           }
  1682.           instance.setCarouselHeight(instance.carouselMainSelected);
  1683.         }, 200, this);
  1684.       }
  1685.  
  1686.       toUpper(text){
  1687.         return text.charAt(0).toUpperCase() + text.slice(1);
  1688.       }
  1689.  
  1690.       unhideDiv(ev){
  1691.         var el = null;
  1692.         if (ev.target) el = ev.target.getAttribute('data-div');
  1693.         else el = ev;
  1694.         var element = this.shadowRoot.querySelector(el);
  1695.         if (element) element.classList.remove('remove');
  1696.         this.setCarouselHeight(this.carouselMainSelected);
  1697.       }
  1698.  
  1699.       hideDiv(ev){
  1700.         var el = null;
  1701.         if (ev.target) el = ev.target.getAttribute('data-div');
  1702.         else el = ev;
  1703.         var element = this.shadowRoot.querySelector(el);
  1704.         if (element) element.classList.add('remove');
  1705.         this.setCarouselHeight(this.carouselMainSelected);
  1706.       }
  1707.  
  1708.       updateTheme(){
  1709.         if (this.alarm.attributes.themes)
  1710.           for (var x in this.alarm.attributes.themes)
  1711.             if (this.alarm.attributes.themes[x].active == true){
  1712.  
  1713.               var theme = this.alarm.attributes.themes[x];
  1714.               if (typeof theme.name == 'string'){
  1715.                 var styles = {};
  1716.                 for (var cssVar in theme){
  1717.                   if (cssVar != 'name' && cssVar != 'active'){
  1718.                     var style = '--' + cssVar.replace(new RegExp('_', 'g'), '-');
  1719.                     styles[style] = this.alarm.attributes.themes[x][cssVar];
  1720.                   }
  1721.                 }
  1722.                 this.updateStyles(styles);
  1723.                 var color = styles['--' + this.alarm.state.replace(new RegExp('_', 'g'), '-') + '-color'];
  1724.                 if (color)
  1725.                   this.updateStyles({'--panel-outer-background-color': color});
  1726.               }
  1727.             }
  1728.       }
  1729.  
  1730.       // Log an error
  1731.       error(message){
  1732.         if (this.errors == null) this.errors = [];
  1733.         this.errors.push(message);
  1734.         console.log(message);
  1735.       }
  1736.  
  1737.       exists(entity){
  1738.         if (entity) return true;
  1739.         else return false;
  1740.       }
  1741.  
  1742.       getObject(entity){
  1743.         return this.hass.states[entity];
  1744.       }
  1745.  
  1746.       getPanelTitle(){
  1747.         if (this.alarm.attributes.panel.panel_title != null && this.alarm.attributes.panel.panel_title != '') this.panel_title = this.alarm.attributes.panel.panel_title;
  1748.       }
  1749.  
  1750.       isChecked(check){
  1751.         if (check == 'True' || check == true) return true;
  1752.         else return false;
  1753.       }
  1754.  
  1755.       computeJSONValue(entity, attribute){
  1756.            if (entity == undefined){
  1757.             return false;
  1758.         } else {
  1759.           var obj = JSON.parse(entity);
  1760.             return obj[attribute];
  1761.         }
  1762.       }
  1763.  
  1764.       computeValueArray(entity, attribute){
  1765.            if (entity == undefined){
  1766.             return false;
  1767.         } else {
  1768.             return entity[attribute];
  1769.         }
  1770.       }
  1771.  
  1772.       computeValueArray(entity, attribute, attribute2){
  1773.            if (entity == undefined){
  1774.             return false;
  1775.         } else {
  1776.             return entity[attribute][attribute2];
  1777.         }
  1778.       }
  1779.  
  1780.       loadSettings(){
  1781.         this.binary_sensors = [];
  1782.  
  1783.         for (var state in this.hass.states) {
  1784.           if (state.split('.')[0] == 'camera'){ //find all cameras and add to array
  1785.             this.cameras.push(state);
  1786.           }
  1787.           else if (state.split('.')[0] == 'binary_sensor'){
  1788.             this.binary_sensors.push(state);
  1789.           }
  1790.         }
  1791.        this.binary_sensors.sort();
  1792.       }
  1793.  
  1794.       computeLog(entry, type){
  1795.         switch (type){
  1796.           case 'time':
  1797.            var t = new Date(entry[0] * 1000);
  1798.             return t.toLocaleString('en-GB', { timeZone: 'UTC' }).split(",")[1] + " - " + t.toLocaleString('en-GB', { timeZone: 'UTC' }).split(",")[0];
  1799.             break;
  1800.           case 'name':
  1801.            for (var user in this.alarm.attributes.users) {
  1802.               if (this.alarm.attributes.users[user]['id'] == entry[1])
  1803.                 return this.toUpper(this.alarm.attributes.users[user]['name']);
  1804.             }
  1805.             return this.toUpper(entry[1]);
  1806.           case 'image':
  1807.            for (var user in this.alarm.attributes.users) {
  1808.               if (this.alarm.attributes.users[user]['id'] == entry[1])
  1809.                 return this.toUpper(this.alarm.attributes.users[user]['picture']);
  1810.             }
  1811.             return '/local/images/ha.png';
  1812.           case 'message':
  1813.            switch (entry[2]){
  1814.               case 0:
  1815.                return 'disarmed the alarm';
  1816.                 break;
  1817.               case 1:
  1818.                return 'Disarm attempt failed, wrong code!';
  1819.                 break;
  1820.               case 2:
  1821.                 return 'The alarm has been triggered! Sensor: ';
  1822.                 break;
  1823.               case 3:
  1824.                return 'set the alarm in Home mode';
  1825.                 break;
  1826.               case 4:
  1827.                return 'set the alarm in Away mode';
  1828.                 break;
  1829.               case 5:
  1830.                 return 'Alarm has been tripped! Sensor: ';
  1831.                 break;
  1832.               case 6:
  1833.                return 'Panel Locked';
  1834.                 break;
  1835.               case 8:
  1836.                return 'set the alarm in Perimeter mode';
  1837.                 break;
  1838.             }
  1839.             break;
  1840.         }
  1841.       }
  1842.  
  1843.       compareString(str1, str2){
  1844.         return true ? str1 == str2 : false;
  1845.       }
  1846.  
  1847.       //Check the admin password to grant access to the settings tab
  1848.       checkSettingsPassword(){
  1849.         //Get the password dom
  1850.         var passwordInput = this.$.passwordInput;
  1851.  
  1852.         //Calculate the SHA256 hash of the input
  1853.         var passwordHash = this.passwordToHash($(passwordInput).val());
  1854.  
  1855.         //If it matches the stored hash then grant access
  1856.         if (passwordHash == this.alarm.attributes.admin_password){
  1857.         //Password matches so load settings cell
  1858.           this.settingsUnlock = true;
  1859.           this.carouselChange(this.shadowRoot.querySelector('#settings-info'));
  1860.  
  1861.           //Clear password input
  1862.           $(passwordInput).val('');
  1863.         }
  1864.       }
  1865.  
  1866.       //Return a sha2656 Hash from the input string
  1867.       passwordToHash(password){
  1868.         return sha256_digest(password);
  1869.       }
  1870.  
  1871.       //Determine the screensize then set the appropriate UI
  1872.       setupUI(){
  1873.  
  1874.         if (this.alarm.attributes.panel == null) {
  1875.           this.alarm.attributes.panel = {};
  1876.           this.settingsUpdate('panel', {});
  1877.         }
  1878.  
  1879.         if (this.alarm.attributes.panel.cameras == null) {
  1880.           var panel = this.alarm.attributes.panel;
  1881.           panel.cameras = [];
  1882.           this.settingsUpdate('panel', panel);
  1883.         }
  1884.  
  1885.         if (this.alarm.attributes.states == null) {
  1886.           var states = {
  1887.             'armed_away' : {'immediate':[], 'delayed':[], 'override':[], 'pending_time': 0, 'warning_time': 0, 'trigger_time': 600},
  1888.             'armed_home' : {'immediate':[], 'delayed':[], 'override':[], 'pending_time': 0, 'warning_time': 0, 'trigger_time': 600},
  1889.             'armed_perimeter' : {'immediate':[], 'delayed':[], 'override':[], 'pending_time': 0, 'warning_time': 0, 'trigger_time': 600}
  1890.           };
  1891.           this.settingsUpdate('states', states);
  1892.         }
  1893.  
  1894.         if (this.alarm.attributes.panel.camera_update_interval > 0) this.camera_update_interval = this.alarm.attributes.panel.camera_update_interval * 1000;
  1895.  
  1896.         this.camTimer = setInterval(() => this.updateCameraTime(), this.camera_update_interval);
  1897.  
  1898.         if (this.isChecked(this.alarm.attributes.panel.enable_weather))
  1899.           this.getWeather();
  1900.  
  1901.         //setupCameras
  1902.         setTimeout(this.setupCameras, 5000, this.shadowRoot);
  1903.  
  1904.         this.setupDt();
  1905.  
  1906.         // Screen less than 1200 px
  1907.         if ($(window).height() < 600) {
  1908.           this.updateStyles({'--shadow-effect': 'unset'});
  1909. /*          if (!this.controlsLoaded) {
  1910.             //Add the keypad and controls to the carousel
  1911.               this.controlsLoaded = true;
  1912.           }*/
  1913.         }
  1914.         else {
  1915.           if (this.isChecked(this.alarm.attributes.panel.shadow_effect)) this.updateStyles({'--shadow-effect': 'below 0 linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, .1))'});
  1916.           else this.updateStyles({'--shadow-effect': 'unset'});
  1917. /*          //Remove the keypad and controls to the carousel
  1918.           this.controlsLoaded = false;*/
  1919.         }
  1920.         if ($(window).width() > 767)
  1921.           if (this.alarm.attributes.panel)
  1922.             this.weatherTimer = setInterval(() => this.updateWeatherSVG(), 500);
  1923.  
  1924.         this.carouselMainSelected = this.$.carousel_template;
  1925.  
  1926.         this.carouselChange(this.$.controls);
  1927.  
  1928.         this.$.carousel_main.style.height = '40vh';
  1929.       }
  1930.  
  1931.       updateWeatherSVG() {
  1932.         if (this.shadowRoot.querySelector('#weather_svg')) {
  1933.           if (this.shadowRoot.querySelector('#weather_svg').getSVGDocument() != null){
  1934.             this.shadowRoot.querySelector('#weather_svg').getSVGDocument().querySelectorAll('svg')[0].setAttribute("fill", window.getComputedStyle(this.shadowRoot.querySelector('#weather-icon')).getPropertyValue("fill"));
  1935.             clearInterval(this.weatherTimer);
  1936.           }
  1937.         }
  1938.       }
  1939.  
  1940.       setupDt(){
  1941.         // Get the modal
  1942.         var modalDT = this.shadowRoot.querySelector('#modalDonate');
  1943.         var dtMethod = this.shadowRoot.querySelector('#donateMethod');
  1944.         var dtAddress = this.shadowRoot.querySelector('#donateAddress');
  1945.  
  1946.         // Get the image and insert it inside the modal
  1947.         var dt = this.shadowRoot.querySelectorAll(".donate");
  1948.  
  1949.         for (var i = 0; i < dt.length; i++) {
  1950.             dt[i].onclick = function(){
  1951.               modalDT.className = "";
  1952.               modalDT.style.display = "block";
  1953.               modalDT.classList.add(this.id);
  1954.  
  1955.               dtMethod.innerHTML = this.querySelector('.info-header').innerHTML;
  1956.               dtAddress.innerHTML = this.getAttribute('data-address');
  1957.             }
  1958.         }
  1959.         // When the user clicks on camera, close the modal
  1960.         modalDT.onclick = function() {
  1961.           modalDT.style.display = "none";
  1962.         }
  1963.       }
  1964.  
  1965.       //One of the menu buttons has been clicked so show the appropriate tab
  1966.       carouselClick(ev){
  1967.         ev.stopPropagation();
  1968.  
  1969.         //Get the selection
  1970.         var selection = ev.target.getAttribute('data-selection');
  1971.  
  1972.         //If the selection is not a settings item then lockup the settings lab
  1973.         if (!selection.includes('settings'))
  1974.           this.settingsUnlock = false;
  1975.  
  1976.         //Show the selected tab
  1977.         this.carouselChange(this.shadowRoot.querySelector('#' + selection));
  1978.       }
  1979.  
  1980.       //Show the newly selected tab
  1981.       carouselChange(carouselCell){
  1982.         if (this.carouselMainSelected != carouselCell){
  1983.           //Remove the current cell
  1984.           this.carouselMainSelected.classList.remove('carousel-slide-in');
  1985.           setTimeout(function(sel) {sel.classList.add('remove');}, 500, this.carouselMainSelected);
  1986.  
  1987.           //Unhide the new cell
  1988.           carouselCell.classList.remove('remove');
  1989.  
  1990.           //Update the parent div height to match the new selection
  1991.           this.setCarouselHeight(carouselCell);
  1992.           //Slidein the new cell
  1993.           setTimeout(function(carouselCell) {carouselCell.classList.add('carousel-slide-in');}, 100, carouselCell);
  1994.           /*carouselCell.classList.add('carousel-slide-in');*/
  1995.  
  1996.           //Update the master selected cell for later use
  1997.           this.carouselMainSelected = carouselCell;
  1998.         }
  1999.       }
  2000.  
  2001.       //Update the parent div height to match the new selection
  2002.       setCarouselHeight(carouselCell, instance){
  2003.         setTimeout(function(carouselCell, instance) {instance.$.carousel_main.style.height = window.getComputedStyle(carouselCell).getPropertyValue("height");}, 50, carouselCell, this);
  2004.       }
  2005.  
  2006.       //Setup the camera clicks and modal
  2007.       setupCameras(root) {
  2008.         // Get the modal
  2009.         var modalCamera = root.querySelector('#modalCamera');
  2010.  
  2011.         // Get the image and insert it inside the modal - use its "alt" text as a caption
  2012.         var camera = root.querySelectorAll(".camera");
  2013.  
  2014.         var imgCamera = root.querySelector('#imgCamera');
  2015.         var captionText = root.querySelector('#captionCamera');
  2016.  
  2017.         for (var i = 0; i < camera.length; i++) {
  2018.           var cam = camera[i];
  2019.             camera[i].onclick = function(){
  2020.               modalCamera.style.display = "block";
  2021.               imgCamera.src = this.src;
  2022.               captionText.innerHTML = this.alt;
  2023.             }
  2024.         }
  2025.         // When the user clicks on camera, close the modal
  2026.         modalCamera.onclick = function() {
  2027.           modalCamera.style.display = "none";
  2028.         }
  2029.       }
  2030.  
  2031.       //Updates camera time
  2032.       updateCameraTime() {
  2033.         this.camera_time = (new Date()).getTime();
  2034.       }
  2035.  
  2036.       //Get the latest camera snapshop
  2037.       updateCameraFeedSrc(camera, camera_time) {
  2038.         if (this.hass.states[camera] == null) return '';
  2039.         var attr = this.hass.states[camera].attributes;
  2040.         return attr.entity_picture + '&time=' + camera_time;
  2041.       }
  2042.  
  2043.       // Close the sidebar if alarm is set and the option is enabled
  2044.       closeSidebar(instance){
  2045.         if (instance) if (instance.alarm.state != 'disarmed')
  2046.           instance.fire('hass-close-menu');
  2047.       }
  2048.  
  2049.       entityTapped(ev){
  2050.         ev.stopPropagation();
  2051.         this.fire('hass-more-info', { entityId: ev.target.getAttribute('data-entity')});
  2052.       }
  2053.  
  2054.       //Monitors the alarm status for any changes
  2055.       monitorAlarm(){
  2056.  
  2057.         var themeName = null;
  2058.  
  2059.         this.opensensors(this.alarm, this.allsensors);
  2060.  
  2061.         this.updateStyles({'--panel-outer-background-color': ''});
  2062.  
  2063.         this.updateTheme();
  2064.  
  2065.         if (this.alarm.attributes.panel)
  2066.           this.updatePanelStyles();
  2067.  
  2068.         //Resets countdown display
  2069.         if (this.alarm.state != 'pending') if (this.$.countdown.querySelector('#countdown360')) this.$.countdown.querySelector('#countdown360').remove();
  2070.  
  2071.         this.updateStyles({'--countdown-timer-display': 'none'});
  2072.         this.updateStyles({'--time-display': 'initial'});
  2073.  
  2074.         if (this.alarm.state == 'disarmed'){
  2075.           if (this.attemptedArm == true) this.resetUI();
  2076.  
  2077.         } else if (this.alarm.state == 'pending'){
  2078.           this.updateStyles({'--countdown-timer-display': 'initial'});
  2079.           this.updateStyles({'--time-display': 'none'});
  2080.           this.loadUITimer(false, this.alarm.attributes.states[this.alarm.attributes.arm_state].pending_time);
  2081.  
  2082.         } else if (this.alarm.state == 'warning'){
  2083.           this.updateStyles({'--countdown-timer-display': 'initial'});
  2084.           this.updateStyles({'--time-display': 'none'});
  2085.           this.loadUITimer(false, this.alarm.attributes.states[this.alarm.attributes.arm_state].warning_time);
  2086.         }
  2087.  
  2088.         if (this.alarm.attributes.panel_locked == true){
  2089.           console.log('Panel locked');
  2090.           this.updateStyles({'--primary-color': 'grey'}); //[TODO] Implement panel locked color - this.alarm.attributes.colors['panel_locked});
  2091.           this.updateStyles({'--countdown-timer-display': 'initial'});
  2092.           this.updateStyles({'--time-display': 'none'});
  2093.           this.loadUITimer(true, this.alarm.attributes.passcode_attempts_timeout);
  2094.           this.panel_locked = true;
  2095.         }
  2096.         else this.panel_locked = false;
  2097.  
  2098.         if (this.carouselMainSelected != null) this.setCarouselHeight(this.carouselMainSelected);
  2099.       }
  2100.  
  2101.       requiresCode(){
  2102.         if (this.alarm.attributes.code_to_arm == false && this.alarm.state == 'disarmed') return false;
  2103.         return true;
  2104.       }
  2105.  
  2106.       updatePanelStyles(){
  2107.         //Re-aligns title bar
  2108.         var title = this.shadowRoot.querySelector('#main-title-text');
  2109.         if (this.alarm.attributes.panel != null && (this.alarm.state == 'disarmed' || this.alarm.attributes.panel.hide_sidebar == false)) title.classList.add('margin-left');
  2110.         else title.classList.remove('margin-left');
  2111.  
  2112.         //Changes the shape of the buttons
  2113.         if (this.isChecked(this.alarm.attributes.panel.round_buttons))
  2114.           this.updateStyles({'--button-shape': '50%'});
  2115.         else this.updateStyles({'--button-shape': '0%'});
  2116.  
  2117.         if (this.isChecked(this.alarm.attributes.panel.hide_sidebar)){
  2118.           clearInterval(this.sidebarTimerId);
  2119.           // Close the sidebar if alarm is set and the option is enabled
  2120.           this.sidebarTimerId = setInterval(this.closeSidebar, 100, this);
  2121.         }
  2122.  
  2123.         //Updates the title of the panel
  2124.         this.getPanelTitle();
  2125.  
  2126.         if (this.isChecked(this.alarm.attributes.panel.enable_serif_font)) this.updateStyles({'--font-header': "'Lobster'"});
  2127.         else this.updateStyles({'--font-header': "unset"});
  2128.       }
  2129.  
  2130.       // Loads the count down timer when arming the alarm
  2131.       loadUITimer(locked, time){
  2132.         var startColor = '#8ac575';
  2133.         var middleColor = 'orange';
  2134.         var endColor = 'red'
  2135.  
  2136.         if (locked == true) startColor = middleColor = endColor = 'red';
  2137.  
  2138.         var countdownDiv = this.$.countdown;
  2139.  
  2140.         $(countdownDiv).empty();
  2141.         $(countdownDiv).unbind().removeData();
  2142.  
  2143.         var countdown = $(countdownDiv).countdown360(
  2144.         {
  2145.           radius      : 35,
  2146.           fontColor   : '#FFFFFF',
  2147.           autostart   : false,
  2148.           label       : false,
  2149.           smooth      : true,
  2150.           seconds     : time,
  2151.           fillStyle_0to50: startColor,
  2152.           fillStyle_50to75: middleColor,
  2153.           fillStyle_75to100: endColor
  2154.         });
  2155.         countdown.start();
  2156.       }
  2157.  
  2158.       // Gets the weather to display if enabled
  2159.       getWeather(){
  2160.         this.weather = this.hass.states['sensor.meteorologia'];
  2161.  
  2162.         //if the above sensor can't be found then try dark_sky
  2163.         if (this.weather == null)
  2164.           this.weather = this.hass.states['sensor.meteorologia'];
  2165.  
  2166.         //No weather entity found display error
  2167.         if (this.weather == null)
  2168.           this.error('Weather entity not found in HA');
  2169.  
  2170.         //set weather temperature
  2171.         if (this.hass.states['sensor.yweather_temperature'])
  2172.           this.temp = Math.ceil((this.hass.states['sensor.yweather_temperature']).state);
  2173.  
  2174.         //if the above sensor is not found look for the dark sky one
  2175.         else if (this.hass.states['sensor.dark_sky_temperature'])
  2176.           this.temp = Math.ceil((this.hass.states['sensor.dark_sky_temperature']).state);
  2177.  
  2178.         //if either isn't found show an error
  2179.         else this.error('Weather temperature entity not found in HA');
  2180.       }
  2181.  
  2182.       // Updates time on the panel if enabled
  2183.       updateTime(hass){
  2184.         if (this.alarm && this.alarm.attributes.panel){
  2185.           if (this.isChecked(this.alarm.attributes.panel.enable_clock)){
  2186.             if (this.hass.states['sensor.time'] == null){
  2187.               this.error('Time Sensor (sensor.time) not found in HA');
  2188.               return;
  2189.             }
  2190.  
  2191.             this.time = this.hass.states['sensor.time'].state;
  2192.  
  2193.             if (this.isChecked(this.alarm.attributes.panel.enable_clock_12hr))
  2194.               if ((parseInt(String(this.time).split(':')[0]) - 12) > 0 )
  2195.                 this.time = String( parseInt( String(this.time).split(':')[0] ) - 12 ) + ":" + String(this.time).split(':')[1];
  2196.           }
  2197.         }
  2198.       }
  2199.  
  2200.       // Is the alarm disarmed?
  2201.       isdisarmed(alarm){
  2202.         return alarm.state == 'disarmed';
  2203.       }
  2204.  
  2205.       // Get open sensors
  2206.       opensensors(alarm, all)   {
  2207.         var ret = [];
  2208.         if (all != null){
  2209.           ret = all.filter(function (e){ return alarm.attributes.supported_statuses_on.indexOf(e.state.toLowerCase()) > -1; });
  2210.           this.opencount = ret.length;
  2211.           return ret;
  2212.         }
  2213.       }
  2214.  
  2215.       // Get closed sensors
  2216.       closedsensors(alarm, all)   {
  2217.         var ret = [];
  2218.         if (all == false){
  2219.           return false;
  2220.         } else {
  2221.           ret = all.filter(function (e){ return alarm.attributes.supported_statuses_off.indexOf(e.state.toLowerCase()) > -1; });
  2222.           return ret;
  2223.         }
  2224.       }
  2225.  
  2226.       // determine whether a sensor is in a particular group
  2227.       computeSensors(hass, ids){
  2228.         if (ids == undefined){
  2229.         // Control the exception when this.alarm is not ready
  2230.           return false;
  2231.         } else {
  2232.           return ids.map(function (key){ return hass.states[key]; }).filter(function (e){ return e != undefined; });
  2233.         }
  2234.       }
  2235.  
  2236.       // Determine if array is empty
  2237.       computeArray(array){
  2238.         if (array.length > 0){
  2239.           return true;
  2240.         }
  2241.         return false;
  2242.       }
  2243.  
  2244.       //Provide a readable label
  2245.       computeLabel(state){
  2246.         switch (state){
  2247.           case 'disarmed':     return 'Off';
  2248.           case 'armed_away':   return 'Fuera';
  2249.           case 'armed_home':   return 'Noche';
  2250.           case 'pending':      return 'Salida';
  2251.           case 'warning':      return 'Atención';
  2252.           case 'triggered':    return 'Disparado';
  2253.           case 'armed_perimeter': return 'Silencioso';
  2254.         }
  2255.         return state;
  2256.       }
  2257.  
  2258.       //Get the friendly readable name of an entity
  2259.       computeFriendlyName(entity) {
  2260.         if (this.hass.states[entity] == null) return 'unknown';
  2261.         var attr = this.hass.states[entity].attributes;
  2262.         return attr.friendly_name;
  2263.         }
  2264.  
  2265.       // Change color of button
  2266.       computeButtonColor(count){
  2267.         if (count > 0) {
  2268.           return 'var(--dark-orange)';
  2269.         }
  2270.         return 'var(--info-panel-buttons)';
  2271.       }
  2272.  
  2273.       // Gets an image from the username
  2274.       computeImage(image){
  2275.         if (image == null || image == '')
  2276.           return '/local/images/ha.png'
  2277.         else if (this.urlExists(image)) return image;
  2278.         else return '/local/images/ha.png';
  2279.       }
  2280.  
  2281.       urlExists(url){
  2282.         try {
  2283.           var http = new XMLHttpRequest();
  2284.           http.open('HEAD', url, false);
  2285.           http.send();
  2286.           return http.status!=404;
  2287.         }
  2288.         catch(err) {
  2289.           return false;
  2290.         }
  2291.       }
  2292.  
  2293.       //Reverses an array
  2294.       reverseArray(array, quantity){
  2295.         if (array) {
  2296.           if (quantity == 'all') quantity = array.length - 1;
  2297.           return array.reverse().slice(0, quantity);
  2298.         }
  2299.       }
  2300.  
  2301.       //Mask the input code if enabled otherwise build up the code from inputs
  2302.       callcode(ev){
  2303.         ev.stopPropagation();
  2304.         var digit = ev.target.getAttribute('data-digit');
  2305.         this.code = this.code + digit;
  2306.         var display_digit = digit;
  2307.         if (this.isChecked(this.alarm.attributes.panel.hide_passcode))
  2308.           display_digit = '*';
  2309.         this.display_code = this.display_code + display_digit;
  2310.       }
  2311.  
  2312.       //Reset the display alarm codes
  2313.       callclearcode(ev){
  2314.         if (ev) ev.stopPropagation();
  2315.         this.code = '';
  2316.         this.display_code = '';
  2317.       }
  2318.  
  2319.       //If you require a code to arm the system then force it here
  2320.       showCodePanel(alarm){
  2321.         if (this.isChecked(alarm.attributes.code_to_arm) || !this.isdisarmed(alarm))
  2322.           return true;
  2323.         else return false;
  2324.       }
  2325.  
  2326.       //An Alarm service has been requested, execute it
  2327.       callAlarmService(ev){
  2328.         ev.stopPropagation();
  2329.  
  2330.         this.showArmPanel   = false;
  2331.         var call            = ev.target.getAttribute('data-call');
  2332.  
  2333.         switch (call){
  2334.           //Check for open sensors before arming the alarm.
  2335.           case 'arm':
  2336.            this.attemptArmMode = ev.target.getAttribute('data-arm');
  2337.             if ( this.checkOpenSensors(this.attemptArmMode) )
  2338.               return;
  2339.             this.alarmService(this.attemptArmMode);
  2340.             break;
  2341.  
  2342.           //The arming of the alarm has been cancelled, reset parameters.
  2343.           case 'cancel':
  2344.            this.attemptArmMode = '';
  2345.             this.resetUI();
  2346.             break;
  2347.  
  2348.           //Open sensors detected but overridden.
  2349.           case 'override':
  2350.            this.alarmService(this.attemptArmMode);
  2351.             this.resetUI();
  2352.             break;
  2353.  
  2354.           //Disarm the alarm.
  2355.           case 'disarm':
  2356.            this.alarmService('alarm_disarm');
  2357.             break;
  2358.  
  2359.         }
  2360.  
  2361.         //Reset the display alarm codes
  2362.         this.callclearcode(null);
  2363.       }
  2364.  
  2365.       //Call one of the alarm services
  2366.       alarmService(call){
  2367.         this.hass.callService('alarm_control_panel', call, {'entity_id': this.alarm.entityId, 'code': this.code });
  2368.       }
  2369.  
  2370.       //Restart Home Assistant
  2371.       restartHA(ev){
  2372.         ev.stopPropagation();
  2373.         this.hass.callService('homeassistant', 'restart');
  2374.       }
  2375.  
  2376.       //enable/disable user
  2377.       toggleUser(ev){
  2378.         var user_id = ev.target.getAttribute('data-userid');
  2379.         this.settingsUpdateUser(user_id, ev.target.checked);
  2380.       }
  2381.  
  2382.       //Show the user edit box and load the selected user
  2383.       editUser(ev){
  2384.         this.hideDiv('#user-add');
  2385.         this.unhideDiv('#userDetail');
  2386.  
  2387.         var user_id = ev.target.getAttribute('data-userid');
  2388.  
  2389.         for (var user in this.alarm.attributes.users) {
  2390.           if (this.alarm.attributes.users[user].id == user_id){
  2391.             //populate user name
  2392.             this.shadowRoot.querySelector('#user-name').value = this.alarm.attributes.users[user].name;
  2393.             //populate enabled toggle
  2394.             this.shadowRoot.querySelector('#user-enabled').checked = this.alarm.attributes.users[user].enabled;
  2395.             //clear pascode
  2396.             this.shadowRoot.querySelector('#user-passcode').value = '';
  2397.             //populate user picture
  2398.             this.shadowRoot.querySelector('#user-picture').value = this.alarm.attributes.users[user].picture;
  2399.             //populate enabled toggle
  2400.             //this.shadowRoot.querySelector('#user-disable_animations').checked = this.alarm.attributes.users[user].disable_animations;
  2401.             //populate permissions
  2402.     /*        this.shadowRoot.querySelector('#user-home-permission').checked = this.alarm.attributes.users[user]['home-permission'];
  2403.             this.shadowRoot.querySelector('#user-away-permission').checked = this.alarm.attributes.users[user]['away-permission'];
  2404.             this.shadowRoot.querySelector('#user-perimeter-permission').checked = this.alarm.attributes.users[user]['perimeter-permission'];*/
  2405.             this.shadowRoot.querySelector('#user-save').setAttribute('data-userID', user_id);
  2406.           }
  2407.         }
  2408.       }
  2409.  
  2410.       //Show the user edit box and load the selected user
  2411.       saveUser(ev){
  2412.         if (this.shadowRoot.querySelector('#user-passcode').value == ''){
  2413.           this.shadowRoot.querySelector('#user-passcode').classList.add('validation-error');
  2414.         }
  2415.         else {
  2416.           var user = {};
  2417.           user['id'] = ev.target.getAttribute('data-userid');
  2418.           user['name'] = this.shadowRoot.querySelector('#user-name').value.toLowerCase();
  2419.           user['enabled'] = this.shadowRoot.querySelector('#user-enabled').checked;
  2420.           user['code'] = this.shadowRoot.querySelector('#user-passcode').value;
  2421.           user['picture'] = this.shadowRoot.querySelector('#user-picture').value;
  2422.           //user['disable_animations'] = this.shadowRoot.querySelector('#user-disable_animations').checked;
  2423.   /*        user[username]['home-permission'] = this.shadowRoot.querySelector('#user-home-permission').checked;
  2424.           user[username]['away-permission'] = this.shadowRoot.querySelector('#user-away-permission').checked;
  2425.           user[username]['perimeter-permission'] = this.shadowRoot.querySelector('#user-perimeter-permission').checked;*/
  2426.           if (ev.target.getAttribute('data-userid') != null)
  2427.             //Update Config
  2428.             this.settingsUpdateUser(user, 'update');
  2429.           else
  2430.             this.settingsUpdateUser(user, 'add');
  2431.  
  2432.           this.resetUserDetail();
  2433.         }
  2434.       }
  2435.  
  2436.       //Show the user edit box and load the selected user
  2437.       deleteUser(ev){
  2438.         var user = ev.target.getAttribute('data-userID');
  2439.  
  2440.         //Update Config
  2441.         this.settingsUpdateUser(user, 'delete');
  2442.         this.resetUserDetail();
  2443.       }
  2444.  
  2445.       //Reset user detail
  2446.       resetUserDetail(){
  2447.         this.hideDiv('#userDetail');
  2448.         this.unhideDiv('#user-add');
  2449.         this.shadowRoot.querySelector('#user-passcode').classList.remove('validation-error')
  2450.         this.shadowRoot.querySelector('#user-name').classList.remove('validation-error')
  2451.  
  2452.         //populate user name
  2453.         this.shadowRoot.querySelector('#user-name').value = '';
  2454.         //populate enabled toggle
  2455.         this.shadowRoot.querySelector('#user-enabled').checked = false;
  2456.         //clear pascode
  2457.         this.shadowRoot.querySelector('#user-passcode').value = '';
  2458.         //populate user picture
  2459.         this.shadowRoot.querySelector('#user-picture').value = '';
  2460.         //populate enabled toggle
  2461.         //this.shadowRoot.querySelector('#user-disable_animations').checked = false;
  2462.       }
  2463.  
  2464.       async _loadData() {
  2465.           this._users = await this.hass.callWS({
  2466.             type: 'config/auth/list',
  2467.           });
  2468.  
  2469.           var userIDs = [];
  2470.           for (var alarmUser in this.alarm.attributes.users)
  2471.             userIDs.push(this.alarm.attributes.users[alarmUser].id);
  2472.  
  2473.           for (var haUser in this._users){
  2474.             if (!userIDs.includes(this._users[haUser].id)){
  2475.               console.log("No match: %s", this._users[haUser].id);
  2476.             //user name
  2477.             var user                               = {};
  2478.  
  2479.             user['id']                             = this._users[haUser].id;
  2480.             user['name']                           = this._users[haUser].name;
  2481.             user['enabled']                        = false;
  2482.             user['code']                           = this._users[haUser].id;
  2483.             user['picture']                        = '/local/images/ha.png';
  2484.             //user['disable_animations']             = false;
  2485. /*              user[username]['home-permission']      = this.shadowRoot.querySelector('#user-home-permission').checked;
  2486.             user[username]['away-permission']      = this.shadowRoot.querySelector('#user-away-permission').checked;
  2487.             user[username]['perimeter-permission'] = this.shadowRoot.querySelector('#user-perimeter-permission').checked;*/
  2488.  
  2489.             this.settingsUpdateUser(user, 'add');
  2490.           }
  2491.         }
  2492.        }
  2493.  
  2494.       loadPreviewImage(ev){
  2495.         var image = ev.target.value;
  2496.         if (!this.urlExists(image)) image = '/local/images/ha.png';
  2497.         this.shadowRoot.querySelector('#user-badge').setAttribute('style', 'background-image: url("' + image + '");margin-top: auto; margin-bottom: auto; margin-right: 15px;');
  2498.       }
  2499.  
  2500.       //Read the settings that are to be updated
  2501.       settingsSave(ev){
  2502.         ev.stopPropagation();
  2503.  
  2504.         //Check the settings tab has been unlocked before commiting any changes
  2505.         if (this.settingsUnlock == true){
  2506.  
  2507.           //Retrieve fields set in the html form
  2508.           var type = ev.target.getAttribute('data-type');
  2509.           var setting = ev.target.getAttribute('data-setting');
  2510.           var attribute = ev.target.getAttribute('data-attribute');
  2511.  
  2512.           var id = ev.target.getAttribute('id');
  2513.           var name = ev.target.getAttribute('name');
  2514.           var title = ev.target.getAttribute('title');
  2515.           var mode = ev.target.getAttribute('data-mode');
  2516.           var sensor_group = ev.target.getAttribute('data-group');
  2517.  
  2518.           var checked = ev.target.checked;
  2519.  
  2520.           var value = null;
  2521.  
  2522.           switch (setting) {
  2523.             case 'panel':
  2524.              if (typeof ev.target.checked === 'boolean')
  2525.                 this.alarm.attributes.panel[attribute] = ev.target.checked;
  2526.               else if (type == 'input_text')
  2527.                 this.alarm.attributes.panel[attribute] = this.shadowRoot.querySelector('#' + attribute).value;
  2528.               else if (type == 'listbox')
  2529.                 this.alarm.attributes.panel[attribute] = this.shadowRoot.querySelector('#' + attribute + '-listbox').selected;
  2530.               this.settingsUpdate(setting, this.alarm.attributes.panel);
  2531.               return;
  2532.  
  2533.             case 'themes':
  2534.              var pos = -1;
  2535.  
  2536.               // If themes dont exist create them
  2537.               if (this.alarm.attributes.themes == null){
  2538.                 this.alarm.attributes.themes = [];
  2539.                 this.settingsUpdate(setting, this.alarm.attributes.themes);
  2540.                 pos = 0
  2541.               }
  2542.  
  2543.               // try and find the selected theme
  2544.               for (var x in this.alarm.attributes.themes)
  2545.                 if (this.alarm.attributes.themes[x].name == name)
  2546.                   pos = x;
  2547.  
  2548.               // if theme isnt selected create one
  2549.               if (name == null){
  2550.                 this.alarm.attributes.themes.push({'name': this.shadowRoot.querySelector('#' + attribute).value});
  2551.                 this.settingsUpdate(setting, this.alarm.attributes.themes);
  2552.                 // this.unhideDiv('#themeDetail');
  2553.                 return;
  2554.               }
  2555.               else if (pos > -1 ){
  2556.                 if (type == 'color'){
  2557.                   this.alarm.attributes.themes[pos][attribute] = '#' + this.shadowRoot.querySelector('#' + attribute).value;
  2558.                   this.settingsUpdate(setting, this.alarm.attributes.themes);
  2559.                   return;
  2560.                 }
  2561.                 else if (type == 'input_text'){
  2562.                   this.alarm.attributes.themes[pos].name = this.shadowRoot.querySelector('#' + attribute).value;
  2563.                   this.settingsUpdate(setting, this.alarm.attributes.themes);
  2564.                   return;
  2565.                 }
  2566.                 else if (type == 'clear'){
  2567.                   this.shadowRoot.querySelector('#' + attribute).value = 'FFFFFF';
  2568.                   this.shadowRoot.querySelector('#' + attribute).setAttribute('style', 'background-color: rgb(255, 255, 255)');                  delete this.alarm.attributes.themes[pos][attribute];
  2569.                   this.settingsUpdate(setting, this.alarm.attributes.themes);
  2570.                   return;
  2571.                 }
  2572.                 else if (type == 'boolean'){
  2573.                   //Disable all others
  2574.                   for (var x in this.alarm.attributes.themes)
  2575.                     this.alarm.attributes.themes[x].active = false;
  2576.                   this.alarm.attributes.themes[pos].active = ev.target.checked;
  2577.                   this.settingsUpdate(setting, this.alarm.attributes.themes);
  2578.                   return;
  2579.                 }
  2580.                 else if (type == 'delete'){
  2581.                   this.alarm.attributes.themes.splice(pos,1);
  2582.                   this.settingsUpdate(setting, this.alarm.attributes.themes);
  2583.                   return;
  2584.                 }
  2585.               }
  2586.               return;
  2587.  
  2588.             case 'mqtt':
  2589.              if (this.alarm.attributes['mqtt'] === 'undefined')
  2590.                 this.alarm.attributes.mqtt = {}
  2591.  
  2592.               if (typeof ev.target.checked === 'boolean')
  2593.                 this.alarm.attributes.mqtt[attribute] = ev.target.checked;
  2594.               else if (type == 'input_text')
  2595.                 this.alarm.attributes.mqtt[attribute] = this.shadowRoot.querySelector('#' + attribute).value;
  2596.               this.settingsUpdate(setting, this.alarm.attributes.mqtt);
  2597.               return;
  2598.  
  2599.             case 'states':
  2600.              if (type == 'input_text')
  2601.                   this.alarm.attributes.states[mode][attribute] = this.shadowRoot.querySelector('#' + mode + '-' + attribute).value;
  2602.  
  2603.               this.settingsUpdate(setting, this.alarm.attributes.states);
  2604.               return;
  2605.  
  2606.             case 'root':
  2607.              if (typeof ev.target.checked === 'boolean')
  2608.                 value = ev.target.checked;
  2609.               else {
  2610.                 value = this.shadowRoot.querySelector('#' + attribute).value;
  2611.                 if (value == ''){
  2612.                   //set css class to red
  2613.                   return;
  2614.                 }
  2615.               }
  2616.  
  2617.               this.settingsUpdate(attribute, value);
  2618.               return;
  2619.  
  2620.             case 'sensor_select':
  2621.              //check if the sensor already exists in the groups
  2622.               var index = this.alarm.attributes.states[mode][sensor_group].indexOf(title);
  2623.  
  2624.               //add sensor to group
  2625.               if (typeof this.alarm.attributes.states[mode][sensor_group][index] === 'undefined')
  2626.                 this.alarm.attributes.states[mode][sensor_group].push(title);
  2627.               //remove sensor from group
  2628.               else if (index >= 0)
  2629.                 this.alarm.attributes.states[mode][sensor_group].splice(index, 1);
  2630.  
  2631.               this.settingsUpdate('states', this.alarm.attributes.states);
  2632.               return;
  2633.  
  2634.             default:
  2635.              return;
  2636.           }
  2637.  
  2638.           this.updateTheme();
  2639.         }
  2640.       }
  2641.  
  2642.       //Call the service to update the yaml file
  2643.       settingsUpdate(setting, value){
  2644.         this.hass.callService('alarm_control_panel', 'ALARM_YAML_SAVE', {'configuration': setting, 'value': value });
  2645.       }
  2646.  
  2647.       //Call the service to update the yaml file
  2648.       settingsUpdateUser(user, command){
  2649.         this.hass.callService('alarm_control_panel', 'ALARM_YAML_USER', {'user': user, 'command': command });
  2650.       }
  2651.  
  2652.       //Check if there are open sensors
  2653.       checkOpenSensors(call){
  2654.         //check to see how many open sensors there are, if none arm alarm
  2655.         if (this.opencount == 0) return false;
  2656.  
  2657.         for (var sensor in this.allsensors){
  2658.           //is it open?
  2659.           //yes
  2660.           if (['on', 'open', 'true', 'detected', 'unlocked'].indexOf(this.allsensors[sensor].state.toLowerCase()) > -1){
  2661.           //is it in the override list?
  2662.             var overrideMode = '';
  2663.             if (call == 'alarm_arm_home') var overrideMode = 'armed_home';
  2664.             if (call == 'alarm_arm_away') var overrideMode = 'armed_away';
  2665.             if (call == 'alarm_arm_night') var overrideMode = 'armed_perimeter';
  2666.  
  2667.             //No: return so display message
  2668.             if (this.alarm.attributes.states[overrideMode].override.indexOf(this.allsensors[sensor].entity_id) == -1){
  2669.  
  2670.               //display the warning message and shake
  2671.               this.attemptedArm = true;
  2672.  
  2673.               this.$.content.classList.add('shake');
  2674.  
  2675.               return true;
  2676.             }
  2677.           }
  2678.         }
  2679.  
  2680.         return false;
  2681.       }
  2682.  
  2683.       //Cancel the attempted alarm and reset UI
  2684.       resetUI(){
  2685.         this.attemptedArm = false;
  2686.  
  2687.         //Remove the page shake from open sensors
  2688.         this.$.content.classList.remove('shake');
  2689.       }
  2690.  
  2691.       // Observer: polymer gaurantees that this won't be called util hass and panel are both defined
  2692.       onPanelUpdate(hass, panel){
  2693.         this.alarm = hass.states[panel.config.alarmid];
  2694.       }
  2695.  
  2696.       fire(type, detail, options) {
  2697.         options = options || {};
  2698.         detail = (detail === null || detail === undefined) ? {} : detail;
  2699.         const event = new Event(type, {
  2700.           bubbles: options.bubbles === undefined ? true : options.bubbles,
  2701.           cancelable: Boolean(options.cancelable),
  2702.           composed: options.composed === undefined ? true : options.composed
  2703.         });
  2704.         event.detail = detail;
  2705.         const node = options.node || this;
  2706.         node.dispatchEvent(event);
  2707.         return event;
  2708.       }
  2709.     }
  2710.  
  2711.     customElements.define(HaPanelAlarm.is, HaPanelAlarm);
  2712.   }
  2713. </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement