Sistema de Alarma para HASS + Xiaomi Gateway

Hola a todos,

En esta entrada voy a detallar como montar un sistema de alarma completo combinando el Gateway de Xiaomi, sus sensores y HomeAssistant.

Voy a dar por sentado que ya tenemos el Gateway integrado en HASS y los sensores configurados.

Yo particularmente siempre trabajo por SSH directamente contra el servidor, así que para todo el proceso utilizaremos PuttY

Nos conectamos por SSH con nuestro usuario homeassistant e ingresamos en el directorio del entorno:

 

 

 

 

En nuestro configuration.yaml agregamos lo siguiente: (nano configuration.yaml)

yaml

  1. alarm_control_panel: !include alarm.yaml
  2. panel_custom: !include panel_custom.yaml

Guardamos (ctrl+o y ctrl+x)

Ahora deberemos crear los archivos que hemos indicado anteriormente.

nano alarm.yaml

yaml

  1. platform: bwalarm
  2. name: House
  3.  
  4. code: !secret alarm_code #Code, should consist of one or more digits ie 6482
  5. panic_code: !secret panic_code #[OPTIONAL] Panic Code should consist of one or more digits ie 9876, it needs to be different to your standard alarm code. This enables a special panic mode. This can be used under duress to deactivate the alarm which would appear to the unseeing eye as deactivated however a special attribute [panic_mode] listed under the alarm_control_panel.[identifier] will change to ACTIVE. This status could be used in your automations to send a notification to someone else police/spouse/sibling/neighbour that you are under duress. To deactive this mode arm then disarm your alarm in the usual manner.
  6.  
  7. pending_time: 25 #Grace time in seconds to allow for exit and entry using Away mode
  8. trigger_time: 600
  9.  
  10. alarm: automation.alarm_triggered
  11. warning: automation.alarm_warning
  12.  
  13. clock: True #Optional - True enables a clock in the center of the status bar
  14. perimeter_mode: False #Optional - True enables perimeter mode, this could be known as 'Day Mode' i.e. only arm the doors whilst there is someone using all floors
  15. weather: True #Optional - Allows a weather summary to be displayed on the status bar. Dark Sky weather component must be enabled with the name sensor.dark_sky_summary
  16.  
  17. #### COLOURS ########### Use any HTML format
  18. warning_colour: 'orange'
  19. pending_colour: 'orange'
  20. disarmed_colour: '#03A9F4'
  21. armed_home_colour: 'black'
  22. armed_away_colour: 'black'
  23. triggered_colour: 'red'
  24.  
  25. ############# SENSOR GROUPS ########################
  26. # Sensors in this group tigger the alarm immediately
  27. immediate:
  28. # - binary_sensor.door_window_sensor_158d0001a5e1a3
  29. # - binary_sensor.door_window_sensor_158d0001a99eb0
  30. - binary_sensor.door_window_sensor_xxxxxxxxxxxx
  31.  
  32. # Sensors in this group start the clock (pending_time) when tripped before the alarm is activated in 'Away' mode
  33. delayed:
  34. - binary_sensor.door_window_sensor_xxxxxxxx
  35. - binary_sensor.door_window_sensor_158d0001a5e1a3
  36. - binary_sensor.door_window_sensor_158d0001a99eb0
  37.  
  38. # Same as notathome but hopefully the title is more self explanatory. Can still use notathome for backwards compatibility
  39. # Note sensors can exist in more than one group notice top_floor appears in two groups
  40. homemodeignore:
  41. - binary_sensor.door_window_sensor_xxxxxxxxx
  42.  
  43. # Use this group to automatically override the warning message on open sensors when setting 'away' mode. (I use this as I have a motion sensor at the front door)
  44. override:
  45. - binary_sensor.door_window_sensor_xxxxxxxxxxxxxxxxxxx
  46.  
  47. # This group is special and only effects 'perimeter mode'. If perimeter_mode is enabled then any sensor in this group will trigger the alarm immediately if arm perimeter is set. There is no delayed group for this mode (unless requested as a feature of course!)
  48. perimeter:
  49. - binary_sensor.garage_door_sensor

Mostrar Código

Y guardamos.

Ahora, nano panel_custom.yaml

yaml

  1. - name: alarm
  2. sidebar_title: Alarm
  3. sidebar_icon: mdi:security-home
  4. config:
  5. alarmid: alarm_control_panel.house ## USE THE SAME ID AS USED IN YOUR ALARM.YAML

Mostrar Código

Y guardamos.

Ahora, creamos el directorio panels.

mkdir panels
cd panels
nano alarm.html

HTML

  1. <script src="/local/lib/jquery-3.2.1.min.js"></script>
  2. <script src="/local/lib/countdown360.js"></script>
  3.  
  4. <style>
  5. @font-face {<br />   font-family: 'Pacifico';<br />   src: url(https://fonts.gstatic.com/s/pacifico/v12/Q_Z9mv4hySLTMoMjnk_rCfesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');<br />}<br />@font-face {<br />   font-family: 'Lobster';<br />   font-style: normal;<br />   font-weight: 400;<br />   src: local('Lobster Regular'), local('Lobster-Regular'), url(https://fonts.gstatic.com/s/lobster/v20/cycBf3mfbGkh66G5NhszPQ.woff2) format('woff2');<br />   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;<br />}<br /></style>
  6.  
  7. &nbsp;
  8.  
  9. <style include="ha-style iron-flex iron-flex-factors">
  10. :host {<br />   --countdown-timer-display: none;<br />   --time-display: initial;<br />}    </p>
  11. <p>       app-header-layout {<br />          background-color: var(--primary-background-color);<br />         }<br />         app-header {<br />             text-align: -webkit-center;<br />        }<br />         app-toolbar ha-menu-button + [main-title] {<br />          margin-left: -50px;<br />        }<br />         #container ::slotted(.content) {<br />             padding: 15px;<br />         }<br />         .shake{<br />          animation: shake linear 1s;<br />           animation-iteration-count: 1;<br />             transform-origin: 50% 50%;<br />            -webkit-animation: shake linear 1s;<br />           -webkit-animation-iteration-count: 1;<br />             -webkit-transform-origin: 50% 50%;<br />            -moz-animation: shake linear 1s;<br />          -moz-animation-iteration-count: 1;<br />            -moz-transform-origin: 50% 50%;<br />           -o-animation: shake linear 1s;<br />            -o-animation-iteration-count: 1;<br />          -o-transform-origin: 50% 50%;<br />             -ms-animation: shake linear 1s;<br />           -ms-animation-iteration-count: 1;<br />             -ms-transform-origin: 50% 50%;<br />         }</p>
  12. <p>       @keyframes shake{<br />            0% {<br />         transform:  translate(0px,0px)  ;<br />          }<br />             10% {<br />            transform:  translate(-10px,0px)  ;<br />            }<br />             20% {<br />            transform:  translate(10px,0px)  ;<br />             }<br />             30% {<br />            transform:  translate(-10px,0px)  ;<br />            }<br />             40% {<br />            transform:  translate(10px,0px)  ;<br />             }<br />             50% {<br />            transform:  translate(-10px,0px)  ;<br />            }<br />             60% {<br />            transform:  translate(10px,0px)  ;<br />             }<br />             70% {<br />            transform:  translate(-10px,0px)  ;<br />            }<br />             80% {<br />            transform:  translate(10px,0px)  ;<br />             }<br />             90% {<br />            transform:  translate(-10px,0px)  ;<br />            }<br />             100% {<br />           transform:  translate(0px,0px)  ;<br />          }<br />          }</p>
  13. <p>       ha-label-badge {<br />             --ha-label-badge-color: rgb(223, 76, 30);<br />          }<br />         :host ::slotted(.badge-container.ha-label-badge) {<br />           margin: 0px 12px;<br />          }<br />         .actions {<br />           margin-left: 20px;<br />            @apply(--layout-self-center);<br />          }<br />         .box {<br />           padding-top: 10px;<br />            padding-bottom: 10px;<br />             display: flex;<br />            justify-content: center; /* align horizontal */<br />           align-items: center; /* align vertical */<br />          }<br />         .box-outer {<br />             margin: 0 5px 10px 5px;<br />           background-color: var(--paper-card-background-color);<br />             border-radius: 3px;<br />           @apply(--shadow-elevation-4dp);<br />        }<br />         .box-inner {<br />             opacity: var(--dark-primary-opacity);<br />             padding: 15px;<br />            margin-right: 15px;<br />        }<br />         .box-sensors {<br />           width: 100%;<br />       }<br />         .title {<br />             font-size: large;<br />             background-color: var(--primary-color);<br />           text-align: -webkit-center;<br />           font-weight: 400;<br />             color: white;<br />             padding: 15px 0px;<br />         }</p>
  14. <p>       .summary {<br />           margin: 0px auto 0px;<br />             text-align: center;<br />        }<br />         /* xs < 768 */<br />        @media screen and (max-width: 767px) {<br />           .sign {<br />          font-size: x-large;<br />            }<br />             #hour, #minute, #middle {<br />            font-size: xx-large;<br />           }<br />             #meridiem {<br />          font-size: 10px;<br />          margin-left: -20px;<br />           margin-right: 3px;<br />             }<br />             #weather-icon img {<br />          width: 40px;<br />           }<br />             .weather {<br />           justify-content: center;<br />          flex-direction: column;<br />           display: flex;<br />             }<br />          }<br />         /* sm */<br />          @media screen and (min-width: 768px) {<br />           .sign {<br />          font-size: xx-large;<br />           }<br />             #hour, #minute, #middle {<br />            font-size: -webkit-xxx-large;<br />          }<br />             #meridiem {<br />          font-size: small;<br />         margin-left: -29px;<br />            }<br />             #weather-icon img {<br />          width: 70px;<br />           }<br />          }</p>
  15. <p>       /* md */<br />          @media screen and (min-width: 992px) {<br />        }</p>
  16. <p>       /* lg */<br />          @media screen and (min-width: 1200px) {<br />       }<br />         .sign {<br />          margin-left: -20px;<br />        }<br />         .ok {<br />            color: green;<br />          }<br />         .warning {<br />           color: orange;<br />         }<br />         .danger {<br />            color: red;<br />        }<br />         .name .active {<br />          color: red;<br />        }<br />         div.box-header {<br />             width: 33%;<br />        }<br />         div.countdown-timer {<br />            display: var(--countdown-timer-display);<br />       }</p>
  17. <p>       div.code {<br />           text-transform: capitalize;<br />        }</p>
  18. <p>       div.code iron-label {<br />            font-size: x-large;<br />        }</p>
  19. <p>       paper-button {<br />           background: #6b8d9c;<br />          color: #ffffff;<br />        }<br />         div.arm paper-button {<br />           width: 100%;<br />          height: 100px;<br />            text-align: -webkit-center;<br />        }<br />         div.arm paper-button.big {<br />           width: 68%;<br />        }<br />         div.arm paper-button.little {<br />            width: 28%;<br />        }</p>
  20. <p>       div.digits paper-button{<br />             width: calc(25%);<br />             height: 77px;<br />             margin-top: 4px;<br />          background: var(--primary-color);<br />             color: var(--primary-background-color);<br />           opacity: 0.9;<br />          }<br />         div.options {<br />            padding-top: 10px;<br />         }<br />         div.options paper-button{<br />            width: 96.5%;<br />          }<br />         .tappable {<br />          cursor: pointer;<br />          margin-top: 2px;<br />          font-size: 1.2em;<br />          }<br />         .time {<br />          color: var( --primary-color);<br />             text-shadow: 1px 1px #6b8d9b;<br />             display: var(--time-display);<br />          }<br />         #middle {<br />            text-align: center;<br />           padding-left: 8px;<br />            margin-right: -5px;<br />        }<br />         #meridiem {<br />          display: inline-block;<br />            vertical-align: middle;<br />           line-height: normal;<br />          height: 100%;<br />             color: #6b8d9b;<br />        }<br />         #weather {<br />           color: var( --primary-color);<br />          }<br />         #weather-icon img {<br />          color: var(--paper-card-background-color);<br />            text-align: -webkit-center;<br />        }<br />         #weather-icon {<br />          height: 100%;<br />          }<br />         .weather-summary span {<br />          display: inline-block;<br />            vertical-align: middle;<br />           line-height: normal;<br />          font-weight: bolder;<br />          margin-left: 5px;<br />             padding-right: 5px;<br />           text-align: -webkit-center;<br />           height: 100%;<br />             font-family: 'Lobster';</p>
  21. <p>       }<br />         #main-title-bar{<br />             display: flex;                  /* establish flex container */<br />            flex-direction: row;            /* default value; can be omitted */<br />           flex-wrap: nowrap;              /* default value; can be omitted */<br />           justify-content: space-between; /* switched from default (flex-start, see below) */<br />           width: 100%;<br />          margin-left: -35px;<br />        }<br />         #main-title-bar div{<br />          }<br />         #main-title-text{<br />            width: 100%;<br />          position: absolute;<br />           margin-left: -25px;<br />        }<br />         #settings-icon{<br />          width: 5%;<br />         }<br />         #settings{<br />           height: calc(100vh - 64px);<br />           display: flex;<br />            flex-direction: row;<br />          flex-wrap: nowrap;<br />            justify-content: space-between;<br />           width: 100%;<br />          text-align: center;<br />        }<br />         #settings-title {<br />            width: 50%;<br />           line-height: calc(100vh - 64px);<br />          color: var(--primary-color);<br />          font-size: 70px;<br />          text-shadow: 3px 3px #6b8d9b;<br />          }<br />         #settings-menu {<br />             width: 50%;<br />           background-color: var(--primary-color);<br />        }<br />         .onoffswitch {<br />           position: relative; width: 90px;<br />          margin-bottom: 25px;<br />          -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;<br />        }<br />         .onoffswitch-checkbox {<br />          display: none;<br />         }<br />         .onoffswitch-label {<br />             display: block; overflow: hidden; cursor: pointer;<br />            border: 2px solid #999999; border-radius: 20px;<br />        }<br />         .onoffswitch-inner {<br />             display: block; width: 200%; margin-left: -100%;<br />          transition: margin 0.3s ease-in 0s;<br />        }<br />         .onoffswitch-inner:before, .onoffswitch-inner:after {<br />            display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;<br />             font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;<br />          box-sizing: border-box;<br />        }<br />         .onoffswitch-inner:before {<br />          content: "ON";<br />            padding-left: 10px;<br />           background-color: var(--primary-color); color: #FFFFFF;<br />           text-align: left;<br />          }<br />         .onoffswitch-inner:after {<br />           content: "OFF";<br />           padding-right: 10px;<br />          background-color: #EEEEEE; color: #999999;<br />            text-align: right;<br />         }<br />         .onoffswitch-switch {<br />            display: block; width: 22px; margin: 4px;<br />             background: #FFFFFF;<br />          position: absolute; top: 0; bottom: 0;<br />            right: 56px;<br />          border: 2px solid #999999; border-radius: 20px;<br />           transition: all 0.3s ease-in 0s;<br />       }<br />         .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {<br />          margin-left: 0;<br />        }<br />         .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {<br />             right: 0px;<br />        }<br />         .blur {<br />          -webkit-filter: blur(10px);<br />           filter: blur(10px);<br />        }<br />         button {<br />             background-color: #03a9f4;<br />            border-width: 0;<br />          color: white;<br />          }<br />         .hide {</p>
  22. <p>       }<br />         .hide-bar {<br />          margin-top: -64px;<br />            height: calc(100vh + 64px);<br />        }<br />         #eyecandy {<br />          height: 100vh;<br />            background: linear-gradient(270deg, #050505, #15305d, #4f5258);<br />           background-size: 400% 400%;<br />           -webkit-animation: backgroundAnimation 30s ease infinite;<br />             -moz-animation: backgroundAnimation 30s ease infinite;<br />            animation: backgroundAnimation 30s ease infinite;<br />          }<br />         @-webkit-keyframes backgroundAnimation {<br />             0%{background-position:0% 50%}<br />            50%{background-position:100% 50%}<br />             100%{background-position:0% 50%}<br />       }<br />         @-moz-keyframes backgroundAnimation {<br />            0%{background-position:0% 50%}<br />            50%{background-position:100% 50%}<br />             100%{background-position:0% 50%}<br />       }<br />         @keyframes backgroundAnimation {<br />             0%{background-position:0% 50%}<br />            50%{background-position:100% 50%}<br />             100%{background-position:0% 50%}<br />       }<br />         .ecTitleDiv {</p>
  23. <p>          padding-top: 20vh;<br />         }</p>
  24. <p>       .ecTitle {<br />           display: block;<br />           margin-bottom: 10px;<br />          font-family: "Avant Garde", Avantgarde, "Century Gothic", CenturyGothic, "AppleGothic", sans-serif;<br />           font-size: -webkit-xxx-large;<br />             text-align: center;<br />           text-transform: uppercase;<br />            text-rendering: optimizeLegibility;<br />           color: #e0dfdc;<br />           letter-spacing: .1em;<br />             text-shadow: 0 -1px 0 #fff, 0 1px 0 #2e2e2e, 0 2px 0 #2c2c2c, 0 3px 0 #2a2a2a, 0 4px 0 #282828, 0 5px 0 #262626, 0 6px 0 #242424, 0 7px 0 #222, 0 8px 0 #202020, 0 9px 0 #1e1e1e, 0 10px 0 #1c1c1c, 0 11px 0 #1a1a1a, 0 12px 0 #181818, 0 13px 0 #161616, 0 14px 0 #141414, 0 15px 0 #121212, 0 22px 30px rgba(0, 0, 0, 0.9);</p>
  25. <p>       }<br />         .ecAlarm {<br />           display: block;<br />           margin-bottom: 10px;<br />          padding-top: 44vh;<br />            font-family: "Avant Garde", Avantgarde, "Century Gothic", CenturyGothic, "AppleGothic", sans-serif;<br />           font-size: xx-large;<br />          text-align: center;<br />           text-transform: uppercase;<br />            text-rendering: optimizeLegibility;<br />           color: #e0dfdc;<br />           letter-spacing: .1em;<br />          }</p>
  26. <p>       .remove {<br />            display: none !important;<br />          }</p>
  27. <p>       #tri {<br />           position: absolute;<br />           top: 50%;<br />             left: 50%;<br />            transform: translate(-50%, 100%);<br />             width: 50px;<br />          height: 50px;<br />          }<br />         #tri .side {<br />             width: 50px;<br />          height: 50px;<br />             position: absolute;<br />           transform-origin: center center;<br />       }<br />         #tri .side:nth-child(1) {<br />            transform: translateX(-100px);<br />         }<br />         #tri .side:nth-child(2) {<br />            transform: translateX(0px);<br />        }<br />         #tri .side:nth-child(3) {<br />            transform: translateX(100px);<br />          }<br />         #tri .side:nth-child(2) {<br />            transform: translateY(-150px);<br />         }<br />         #tri .ring {<br />             width: 50px;<br />          height: 50px;<br />             position: absolute;<br />           background-color: black;<br />          animation-name: rotate, cromatic;<br />             animation-duration: 3s, 3s;<br />           animation-timing-function: linear, linear;<br />            animation-iteration-count: infinite, infinite;<br />            opacity: 0.5;<br />             box-shadow: 0 0 30px black;<br />           border-radius: 20%;<br />        }</p>
  28. <p>       .side:nth-child(1) .ring:nth-child(0) {<br />          margin: 0 0 0 0px;<br />            z-index: 0;<br />           animation-delay: 0s;<br />       }</p>
  29. <p>       .side:nth-child(1) .ring:nth-child(1) {<br />          margin: 0 0 0 10px;<br />           z-index: 1;<br />           animation-delay: 0.05s;<br />        }</p>
  30. <p>       .side:nth-child(1) .ring:nth-child(2) {<br />          margin: 0 0 0 20px;<br />           z-index: 2;<br />           animation-delay: 0.1s;<br />         }</p>
  31. <p>       .side:nth-child(1) .ring:nth-child(3) {<br />          margin: 0 0 0 30px;<br />           z-index: 3;<br />           animation-delay: 0.15s;<br />        }</p>
  32. <p>       .side:nth-child(1) .ring:nth-child(4) {<br />          margin: 0 0 0 40px;<br />           z-index: 4;<br />           animation-delay: 0.2s;<br />         }</p>
  33. <p>       .side:nth-child(1) .ring:nth-child(5) {<br />          margin: 0 0 0 50px;<br />           z-index: 5;<br />           animation-delay: 0.25s;<br />        }</p>
  34. <p>       .side:nth-child(1) .ring:nth-child(6) {<br />          margin: 0 0 0 60px;<br />           z-index: 6;<br />           animation-delay: 0.3s;<br />         }</p>
  35. <p>       .side:nth-child(1) .ring:nth-child(7) {<br />          margin: 0 0 0 70px;<br />           z-index: 7;<br />           animation-delay: 0.35s;<br />        }</p>
  36. <p>       .side:nth-child(1) .ring:nth-child(8) {<br />          margin: 0 0 0 80px;<br />           z-index: 8;<br />           animation-delay: 0.4s;<br />         }</p>
  37. <p>       .side:nth-child(1) .ring:nth-child(9) {<br />          margin: 0 0 0 90px;<br />           z-index: 9;<br />           animation-delay: 0.45s;<br />        }</p>
  38. <p>       .side:nth-child(1) .ring:nth-child(10) {<br />             margin: 0 0 0 100px;<br />          z-index: 10;<br />          animation-delay: 0.5s;<br />         }</p>
  39. <p>       .side:nth-child(2) .ring:nth-child(0) {<br />          margin: 0px 0 0 0px;<br />          z-index: 20;<br />          animation-delay: 2s;<br />       }</p>
  40. <p>       .side:nth-child(2) .ring:nth-child(1) {<br />          margin: 7.5px 0 0 -5px;<br />           z-index: 19;<br />          animation-delay: 2.05s;<br />        }</p>
  41. <p>       .side:nth-child(2) .ring:nth-child(2) {<br />          margin: 15px 0 0 -10px;<br />           z-index: 18;<br />          animation-delay: 2.1s;<br />         }</p>
  42. <p>       .side:nth-child(2) .ring:nth-child(3) {<br />          margin: 22.5px 0 0 -15px;<br />             z-index: 17;<br />          animation-delay: 2.15s;<br />        }</p>
  43. <p>       .side:nth-child(2) .ring:nth-child(4) {<br />          margin: 30px 0 0 -20px;<br />           z-index: 16;<br />          animation-delay: 2.2s;<br />         }</p>
  44. <p>       .side:nth-child(2) .ring:nth-child(5) {<br />          margin: 37.5px 0 0 -25px;<br />             z-index: 15;<br />          animation-delay: 2.25s;<br />        }</p>
  45. <p>       .side:nth-child(2) .ring:nth-child(6) {<br />          margin: 45px 0 0 -30px;<br />           z-index: 14;<br />          animation-delay: 2.3s;<br />         }</p>
  46. <p>       .side:nth-child(2) .ring:nth-child(7) {<br />          margin: 52.5px 0 0 -35px;<br />             z-index: 13;<br />          animation-delay: 2.35s;<br />        }</p>
  47. <p>       .side:nth-child(2) .ring:nth-child(8) {<br />          margin: 60px 0 0 -40px;<br />           z-index: 12;<br />          animation-delay: 2.4s;<br />         }</p>
  48. <p>       .side:nth-child(2) .ring:nth-child(9) {<br />          margin: 67.5px 0 0 -45px;<br />             z-index: 11;<br />          animation-delay: 2.45s;<br />        }</p>
  49. <p>       .side:nth-child(2) .ring:nth-child(10) {<br />             margin: 75px 0 0 -50px;<br />           z-index: 10;<br />          animation-delay: 2.5s;<br />         }</p>
  50. <p>       .side:nth-child(3) .ring:nth-child(0) {<br />          margin: 0px 0 0 0px;<br />          z-index: 0;<br />           animation-delay: 1s;<br />       }</p>
  51. <p>       .side:nth-child(3) .ring:nth-child(1) {<br />          margin: -7.5px 0 0 -5px;<br />          z-index: 1;<br />           animation-delay: 1.05s;<br />        }</p>
  52. <p>       .side:nth-child(3) .ring:nth-child(2) {<br />          margin: -15px 0 0 -10px;<br />          z-index: 2;<br />           animation-delay: 1.1s;<br />         }</p>
  53. <p>       .side:nth-child(3) .ring:nth-child(3) {<br />          margin: -22.5px 0 0 -15px;<br />            z-index: 3;<br />           animation-delay: 1.15s;<br />        }</p>
  54. <p>       .side:nth-child(3) .ring:nth-child(4) {<br />          margin: -30px 0 0 -20px;<br />          z-index: 4;<br />           animation-delay: 1.2s;<br />         }</p>
  55. <p>       .side:nth-child(3) .ring:nth-child(5) {<br />          margin: -37.5px 0 0 -25px;<br />            z-index: 5;<br />           animation-delay: 1.25s;<br />        }</p>
  56. <p>       .side:nth-child(3) .ring:nth-child(6) {<br />          margin: -45px 0 0 -30px;<br />          z-index: 6;<br />           animation-delay: 1.3s;<br />         }</p>
  57. <p>       .side:nth-child(3) .ring:nth-child(7) {<br />          margin: -52.5px 0 0 -35px;<br />            z-index: 7;<br />           animation-delay: 1.35s;<br />        }</p>
  58. <p>       .side:nth-child(3) .ring:nth-child(8) {<br />          margin: -60px 0 0 -40px;<br />          z-index: 8;<br />           animation-delay: 1.4s;<br />         }</p>
  59. <p>       .side:nth-child(3) .ring:nth-child(9) {<br />          margin: -67.5px 0 0 -45px;<br />            z-index: 9;<br />           animation-delay: 1.45s;<br />        }</p>
  60. <p>       .side:nth-child(3) .ring:nth-child(10) {<br />             margin: -75px 0 0 -50px;<br />          z-index: 10;<br />          animation-delay: 1.5s;<br />         }</p>
  61. <p>       @keyframes rotate1 {<br />             to {<br />         transform: rotate(360deg);<br />             }<br />          }<br />         @keyframes cromatic {<br />            0% {<br />         background-color: #870000;<br />             }<br />             30% {<br />            background-color: #290f6b;<br />             }<br />             60% {<br />            background-color: #290f6b;<br />             }<br />             100% {<br />           background-color: #870000;<br />             }<br />          }<br />         .view {<br />          position: relative;<br />           top: 160px;<br />           left: 0;<br />          right: 0;<br />             bottom: 0;<br />            -webkit-perspective: 400;<br />             perspective: 400;<br />          }<br />         .plane {<br />             width: 120px;<br />             height: 120px;<br />            -webkit-transform-style: preserve-3d;<br />             transform-style: preserve-3d;<br />          }<br />         .plane.main {<br />            position: absolute;<br />           top: 0;<br />           left: 0;<br />          right: 0;<br />             bottom: 0;<br />            margin: auto;<br />             -webkit-transform: rotateX(60deg) rotateZ(-30deg);<br />            transform: rotateX(60deg) rotateZ(-30deg);<br />            -webkit-animation: rotate 20s infinite linear;<br />            animation: rotate 20s infinite linear;<br />         }<br />         .plane.main .circle {<br />            width: 120px;<br />             height: 120px;<br />            position: absolute;<br />           -webkit-transform-style: preserve-3d;<br />             transform-style: preserve-3d;<br />             border-radius: 100%;<br />          -webkit-box-sizing: border-box;<br />           box-sizing: border-box;<br />           -webkit-box-shadow: 0 0 60px white, inset 0 0 60px white;<br />             box-shadow: 0 0 60px white, inset 0 0 60px white;<br />          }<br />         .plane.main .circle:nth-child(1) {<br />           -webkit-transform: rotateZ(72deg) rotateX(63.435deg);<br />             transform: rotateZ(72deg) rotateX(63.435deg);<br />          }<br />         .plane.main .circle:nth-child(2) {<br />           -webkit-transform: rotateZ(144deg) rotateX(63.435deg);<br />            transform: rotateZ(144deg) rotateX(63.435deg);<br />         }<br />         .plane.main .circle:nth-child(3) {<br />           -webkit-transform: rotateZ(216deg) rotateX(63.435deg);<br />            transform: rotateZ(216deg) rotateX(63.435deg);<br />         }<br />         .plane.main .circle:nth-child(4) {<br />           -webkit-transform: rotateZ(288deg) rotateX(63.435deg);<br />            transform: rotateZ(288deg) rotateX(63.435deg);<br />         }<br />         .plane.main .circle:nth-child(5) {<br />           -webkit-transform: rotateZ(360deg) rotateX(63.435deg);<br />            transform: rotateZ(360deg) rotateX(63.435deg);<br />         }</p>
  62. <p>       @-webkit-keyframes rotate {<br />          0% {<br />         -webkit-transform: rotateX(0) rotateY(0) rotateZ(0);<br />          transform: rotateX(0) rotateY(0) rotateZ(0);<br />           }<br />             100% {<br />           -webkit-transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);<br />           transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);<br />            }<br />          }</p>
  63. <p>       @keyframes rotate {<br />          0% {<br />         -webkit-transform: rotateX(0) rotateY(0) rotateZ(0);<br />          transform: rotateX(0) rotateY(0) rotateZ(0);<br />           }<br />             100% {<br />           -webkit-transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);<br />           transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);<br />            }<br />          }<br />      </style>
  64.  
  65. &nbsp;
  66. <div id="main-title-bar">
  67. <div></div>
  68. <div id="main-title-text">  Alarma de Casa</div>
  69. <div id="settings-icon" style="display: none;"></div>
  70. </div>
  71. &nbsp;
  72. <div class="content">
  73. <div id="alarm-panel">
  74. <div class="horizontal layout center-justified">
  75. <div class="box zone-card box-header"></div>
  76. <div class="box zone-card box-header">
  77. <div id="time" class="time"><span id="hour">[[hours]]</span>
  78. <span id="middle">:
  79. <span id="meridiem">[[meridiem]]</span>
  80. </span>
  81. <span id="minute">[[minutes]]</span></div>
  82. &nbsp;
  83. <div id="countdown" class="countdown-timer"></div>
  84. </div>
  85. <div class="box zone-card box-header weather">
  86. <div id="weather-icon" class="weather-summary">[[temp]] °C
  87. <img src="[[weather.attributes.entity_picture]]" /></div>
  88. <div class="weather-summary">[[weather.state]]</div>
  89. &nbsp;
  90.  
  91. </div>
  92. </div>
  93. &nbsp;
  94. <div class="statecard">
  95. <div class="title arm">  Alarma Desactivada</div>
  96. <div class="box arm zone-card">Perimeter Mode
  97. Modo Noche Modo Fuera de Casa</div>
  98. </div>
  99. &nbsp;
  100.  
  101. &nbsp;
  102. <div class="statecard">
  103. <div class="box-outer">
  104. <div class="title code">[[computeLabel(alarm.state)]] Mode Activated   [[code]]</div>
  105. <div class="horizontal layout center-justified">
  106. <div style="width: 100%;">
  107. <div class="digits horizontal layout center-justified">123Desarmar</div>
  108. <div class="digits horizontal layout center-justified">456Borrar</div>
  109. <div class="digits horizontal layout center-justified">7890</div>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. &nbsp;
  115. <div class="statecard">
  116.  
  117. &nbsp;
  118. <div class="box-outer" style="max-width: 100%;">
  119. <div class="title" style="background-color: orange;">[[opencount]]x Open Sensors</div>
  120. <div class="box-inner"></div>
  121. </div>
  122. &nbsp;
  123. <div class="horizontal layout center-justified">
  124.  
  125. &nbsp;
  126. <div class="box-outer box-sensors">
  127. <div class="title">Sensores Inmediatos</div>
  128. <div class="box-inner"></div>
  129. </div>
  130. &nbsp;
  131.  
  132. &nbsp;
  133. <div class="box-outer box-sensors">
  134. <div class="title">Sensores Retrasados</div>
  135. <div class="box-inner"></div>
  136. </div>
  137. <div class="box-outer box-sensors">
  138. <div class="title">Sensores Inmediatos</div>
  139. <div class="box-inner"></div>
  140. </div>
  141. &nbsp;
  142.  
  143. </div>
  144. <div class="box-outer" style="max-width: 100%;">
  145. <div class="title">Todos los Sensores</div>
  146. &nbsp;
  147. <div class="title">Sensores Inactivos</div>
  148. &nbsp;
  149. <div class="box-inner">
  150.  
  151. &nbsp;
  152.  
  153. &nbsp;
  154.  
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. <div id="settings" class="remove">
  160. <div id="settings-title">Settings</div>
  161. <div id="settings-menu">
  162. <div class="onoffswitch"><input id="screensaverSwitch" class="onoffswitch-checkbox" checked="checked" name="onoffswitch" type="checkbox" /></div>
  163. <div class="onoffswitch"><input id="otherSwitch" class="onoffswitch-checkbox" checked="checked" name="onoffswitch" type="checkbox" /></div>
  164. </div>
  165. </div>
  166. <div id="eyecandy" class="remove">
  167. <div class="ecTitleDiv"><span class="ecTitle">Calvert</span>
  168. <span class="ecTitle">Residence</span></div>
  169. <div class="view">
  170. <div class="plane main">
  171. <div class="circle"></div>
  172. <div class="circle"></div>
  173. <div class="circle"></div>
  174. <div class="circle"></div>
  175. <div class="circle"></div>
  176. <div class="circle"></div>
  177. </div>
  178. </div>
  179. <div class="ecAlarm">Disarmed</div>
  180. </div>
  181. </div>
  182. &nbsp;
  183.  
  184. <script>
  185. Polymer({
  186.    is: 'ha-panel-alarm',
  187.    attached() {
  188.       this.onAttached();
  189.    },
  190.    ready() {
  191.       //if (this.screensaver){
  192.       //     this.setupEyeCandy();
  193.       //     }
  194.    },
  195.    properties: {
  196.       // things that appear in all the other panels
  197.       hass:     { type: Object },
  198.       panel:    { type: Object },
  199.       narrow:   { type: Boolean, value: false },
  200.       showMenu: { type: Boolean, value: false },
  201.       // things specific to this alarm panel
  202.       alarm:      { type: Object, observer: 'changeTheme' },
  203.       immediate:  { type: Array, computed: 'computeSensors(hass, alarm.attributes.immediate)' },
  204.       delayed:    { type: Array, computed: 'computeSensors(hass, alarm.attributes.delayed)' },
  205.       ignored:    { type: Array, computed: 'computeSensors(hass, alarm.attributes.ignored)' },
  206.       perimeter:  { type: Array, computed: 'computeSensors(hass, alarm.attributes.perimeter)' },
  207.       allsensors: { type: Array, computed: 'computeSensors(hass, alarm.attributes.allsensors)' },
  208.       overrideSensors: { type: Array, computed: 'computeSensors(hass, alarm.attributes.override)' },
  209.       weather:    { type: String },
  210.       temp:   { type: String },
  211.       code:       { type: String, value: '' },
  212.       opencount:  { type: Number, value: 0 },
  213.       timeoutID:  { type: Number },
  214.       hours:      { type: String, value: '??' },
  215.       minutes:    { type: String, value: '??' },
  216.       meridiem:   { type: String, value: '?M' },
  217.       // Hold our unobservers
  218.       cleanup:    { type: Array, value: [] },
  219.       attemptedArm:   { type: Boolean, value: false },
  220.       screensaver:    { type: Boolean, value: false },
  221.       settings:       { type: Boolean, value: false },
  222.    },
  223.  
  224.    changeTheme: function() {
  225.       //    console.log(this.alarm.attributes.warning_colour);
  226.       this.updateStyles({'--countdown-timer-display': 'none'});
  227.       this.updateStyles({'--time-display': 'initial'});
  228.       if (this.alarm.state == 'disarmed') {
  229.      if (this.attemptedArm == true) { this.resetButtons(); }
  230.      this.updateStyles({'--primary-color': this.alarm.attributes.disarmed_colour});
  231.       } else if (this.alarm.state == 'armed_away') {
  232.      this.updateStyles({'--primary-color': this.alarm.attributes.armed_away_colour});
  233.       } else if ( this.alarm.state == 'armed_home') {
  234.      this.updateStyles({'--primary-color': this.alarm.attributes.armed_home_colour});
  235.       } else if ( this.alarm.state == 'armed_perimeter') {
  236.      this.updateStyles({'--primary-color': this.alarm.attributes.armed_home_colour}); //SORT THIS OUT
  237.       } else if (this.alarm.state == 'pending') {
  238.      this.updateStyles({'--primary-color': this.alarm.attributes.pending_colour});
  239.      this.updateStyles({'--countdown-timer-display': 'initial'});
  240.      this.updateStyles({'--time-display': 'none'});
  241.      this.loadTimer();
  242.       } else if (this.alarm.state == 'warning') {
  243.      this.updateStyles({'--primary-color': this.alarm.attributes.warning_colour});
  244.      this.loadTimer();
  245.       } else if (this.alarm.state == 'triggered') {
  246.      this.updateStyles({'--primary-color': this.alarm.attributes.triggered_colour});
  247.       }
  248.    },
  249.    loadTimer: function() {
  250.       var countdownDiv = Polymer.dom(this.root).querySelector('#countdown');
  251.       var countdown = $(countdownDiv).countdown360({
  252.      radius      : 50,
  253.      seconds     : this.alarm.attributes.countdown_time,
  254.      fontColor   : '#FFFFFF',
  255.      autostart   : false,
  256.      label       : false,
  257.      smooth      : true
  258.         //onComplete  : function () { console.log('done') }
  259.       });
  260.       countdown.stop();
  261.       countdown.start();
  262.    },
  263.    onAttached: function() {
  264.       if (this.alarm.attributes.clock) {
  265.      this.updateTime();
  266.       }
  267.       if (this.alarm.attributes.weather) {
  268.      this.getWeather();
  269.       }
  270.    },
  271.    //--------------------------------------------------------------//
  272.    //-----------------EYE CANDY FUNCTION---------------------------//
  273.    //--------------------------------------------------------------//
  274.    setupEyeCandy: function () {
  275.       this.addEventListener("mousemove", this.resetTimer);
  276.       this.addEventListener("mousedown", this.resetTimer);
  277.       this.addEventListener("keypress", this.resetTimer);
  278.       this.addEventListener("DOMMouseScroll", this.resetTimer);
  279.       this.addEventListener("mousewheel", this.resetTimer);
  280.       this.addEventListener("touchmove", this.resetTimer);
  281.       this.addEventListener("MSPointerMove", this.resetTimer);
  282.  
  283.       this.startTimer();
  284.    },
  285.    startTimer: function() {
  286.       this.timeoutID = this.async(this.goInactive,8000);
  287.    },
  288.    resetTimer: function () {
  289.       clearTimeout(this.timeoutID);
  290.       this.goActive();
  291.    },
  292.    goActive: function() {
  293.       var alarmContent = Polymer.dom(this.root).querySelector('#alarm-panel');
  294.       alarmContent.classList.remove('remove');
  295.       var eyecandyContent = Polymer.dom(this.root).querySelector('#eyecandy');
  296.       eyecandyContent.classList.add('remove');
  297.       var appHeader = Polymer.dom(this.root).querySelector('app-header-layout');
  298.       appHeader.classList.remove('hide-bar');
  299.       var settingsContent = Polymer.dom(this.root).querySelector('#settings');
  300.       settingsContent.classList.add('remove');
  301.  
  302.       this.startTimer();
  303.    },
  304.    goInactive: function () {
  305.       var alarmContent = Polymer.dom(this.root).querySelector('#alarm-panel');
  306.       alarmContent.classList.add('remove');
  307.       var eyecandyContent = Polymer.dom(this.root).querySelector('#eyecandy');
  308.       eyecandyContent.classList.remove('remove');
  309.       var appHeader = Polymer.dom(this.root).querySelector('app-header-layout');
  310.       appHeader.classList.add('hide-bar');
  311.       var settingsContent = Polymer.dom(this.root).querySelector('#settings');
  312.       settingsContent.classList.remove('remove');
  313.    },
  314.    //--------------------------------------------------------------//
  315.    //-----------------EYE CANDY FUNCTION---------------------------//
  316.    //--------------------------------------------------------------//
  317.    getWeather: function() {
  318.       this.weather = this.hass.states['sensor.clima_actual'];
  319.       this.temp = Math.ceil((this.hass.states['sensor.yr_temperature']).state);
  320.    },
  321.    toggleSettings() {
  322.       var settingsContent = Polymer.dom(this.root).querySelector('#settings');
  323.       var alarmContent = Polymer.dom(this.root).querySelector('#alarm-panel');
  324.       if (this.settings) {
  325.      alarmContent.classList.remove('remove');
  326.      settingsContent.classList.add('remove');
  327.      this.settings = false;
  328.       } else {
  329.      alarmContent.classList.add('remove');
  330.      settingsContent.classList.remove('remove');
  331.      this.settings = true;
  332.       }
  333.    },
  334.    updateTime: function() {
  335.       var timeNow = new Date();
  336.       var hours   = timeNow.getHours();
  337.       var minutes = timeNow.getMinutes();
  338.       var meridiem = (hours >= 12) ? " P.M" : " A.M";
  339.       this.hours = ("0" + hours).slice(-2);
  340.       this.minutes = ("0" + minutes).slice(-2);
  341.       this.meridiem = meridiem;
  342.       this.async(this.updateTime,1000);
  343.    },
  344.    // Polymer observers definition
  345.    observers: [ 'onPanelUpdate(hass, panel)' ],
  346.    wtf: function (e) { debugger; return e; },
  347.    // Helpers to figure out what to display
  348.    isdisarmed:  function(alarm) { return alarm.state == "disarmed"; },
  349.    isperimeter:  function(alarm) { return alarm.state == "armed_perimeter"; },
  350.    issecure:    function(all)   { return this.opensensors(all).length == 0; },
  351.    opensensors: function(all)   {
  352.       if (all == false) {
  353.      return false;
  354.       } else {
  355.      ret = all.filter(function (e) { return ['on', 'open', 'true', 'detected', 'unlocked'].indexOf(e.state.toLowerCase()) > -1; });
  356.      this.opencount = ret.length;
  357.      return ret;
  358.       }
  359.    },
  360.    closedsensors: function(all)   {
  361.       if (all == false) {
  362.      return false;
  363.       } else {
  364.      ret = all.filter(function (e) { return ['off', 'closed', 'false', 'undetected', 'locked'].indexOf(e.state.toLowerCase()) > -1; });
  365.      return ret;
  366.       }
  367.    },
  368.    computeSensors: function(hass, ids) {
  369.       if (ids == undefined) {
  370.      // Control the exception when this.alarm is not ready
  371.      return false;
  372.       } else {
  373.      return ids.map(function (key) { return hass.states[key]; }).filter(function (e) { return e != undefined; });
  374.       }
  375.    },
  376.    computeIcon: function (state) {
  377.       switch (state) {
  378.      case 'disarmed':        return 'mdi:shield-outline';
  379.      case 'armed_away':      return 'mdi:security-home';
  380.      case 'armed_home':      return 'mdi:security-home';
  381.      case 'pending':         return 'mdi:walk';
  382.      case 'warning':         return 'mdi:run';
  383.      case 'triggered':       return 'mdi:alert-circle';
  384.      case 'armed_perimeter': return 'mdi:security-home';
  385.       }
  386.       return 'mdi:help';
  387.    },
  388.    computeLabel: function (state) {
  389.       switch (state) {
  390.      case 'disarmed':        return 'off';
  391.      case 'armed_away':      return 'away';
  392.      case 'armed_home':      return 'home';
  393.      case 'pending':         return 'leave';
  394.      case 'warning':         return 'warn';
  395.      case 'triggered':       return 'alarm';
  396.      case 'armed_perimeter': return 'perimeter';
  397.       }
  398.       return state;
  399.    },
  400.    // Responding to user inputs
  401.    callcode: function(ev) {
  402.       ev.stopPropagation();
  403.       var digit = ev.target.getAttribute('data-digit');
  404.       this.code = this.code + digit;
  405.       //console.log(this.code);
  406.    },
  407.    callclearcode: function(ev) {
  408.       ev.stopPropagation();
  409.       this.code = '';
  410.       //console.log('Code Cleared: ' + this.code);
  411.    },
  412.    callService: function(ev) {
  413.       ev.stopPropagation();
  414.       var call = ev.target.getAttribute('data-call');
  415.       if (call == 'cancel') { //cancel alarm set and return page to default settings
  416.      this.resetButtons();
  417.       } else if (ev.target.getAttribute('data-override')) { //Override of open sensors has been pressed so activate alarm
  418.      this.hass.callService('alarm_control_panel', call, {'entity_id': this.alarm.entityId, 'code': this.code})
  419.       } else if ((call == 'alarm_arm_home' || call == 'alarm_arm_away' || call == 'alarm_arm_night') && this.checkOpenSensors(call)) { //Check if trying to activate home with open sensors. If so ping the warning message
  420.      console.log('WARNING open sensors');
  421.       } else { //No open sensors or another service call
  422.      this.hass.callService('alarm_control_panel', call, {'entity_id': this.alarm.entityId, 'code': this.code});
  423.      this.code = '';
  424.       }
  425.    },
  426.    checkOpenSensors: function(call) {
  427.       if (this.opencount == 0) return false; //check to see how many open sensors there are, if none arm alarm
  428.  
  429.       for (var sensor in this.allsensors) { //check to see if the open sensor is one to ignore, if so arm alarm
  430.      //is it open?
  431.      if (['on', 'open', 'true', 'detected', 'unlocked'].indexOf(this.allsensors[sensor].state.toLowerCase()) > -1) { //yes
  432.         //is it in the override list?
  433.         if (this.overrideSensors.indexOf(this.allsensors[sensor]) == -1) { //No: return so display message
  434.            this.attemptedArm = true; //display the warning message and shake
  435.  
  436.            var btnArmHome      = Polymer.dom(this.root).querySelector('#arm-home');
  437.            var btnArmAway      = Polymer.dom(this.root).querySelector('#arm-away');
  438.  
  439.            if (this.alarm.attributes.perimeter_mode) {
  440.           var btnArmPerimeter = Polymer.dom(this.root).querySelector('#arm-perimeter');
  441.           btnArmPerimeter.classList.add('remove');
  442.            }
  443.            btnArmHome.classList.add('big');
  444.            btnArmAway.classList.add('little');
  445.  
  446.            this.override = document.createAttribute("data-override");
  447.            this.override.value = "true";
  448.            btnArmHome.setAttributeNode(this.override);
  449.  
  450.            var arm_mode = document.createAttribute("data-call");
  451.            arm_mode.value = call;
  452.            btnArmHome.setAttributeNode(arm_mode);
  453.  
  454.            var arm_mode2 = document.createAttribute("data-call");
  455.            arm_mode2.value = 'cancel';
  456.            btnArmAway.setAttributeNode(arm_mode2);
  457.            btnArmHome.innerHTML = 'Override Sensors and Arm Alarm';
  458.            btnArmAway.innerHTML = 'Cancel Arm';
  459.  
  460.            var pageContent = Polymer.dom(this.root).querySelector('.content');
  461.            pageContent.classList.add('shake');
  462.  
  463.            return true;
  464.  
  465.         }
  466.      }          
  467.       }
  468.    },
  469.    resetButtons: function() {
  470.       this.attemptedArm = false;
  471.  
  472.       var pageContent = Polymer.dom(this.root).querySelector('.content');
  473.       pageContent.classList.remove('shake');
  474.  
  475.       var btnArmHome      = Polymer.dom(this.root).querySelector('#arm-home');
  476.       var btnArmAway      = Polymer.dom(this.root).querySelector('#arm-away');
  477.       if (this.alarm.attributes.perimeter_mode) {
  478.      var btnArmPerimeter = Polymer.dom(this.root).querySelector('#arm-perimeter');
  479.      btnArmPerimeter.classList.remove('remove');
  480.       }
  481.       btnArmHome.classList.remove('big');
  482.       btnArmAway.classList.remove('little');
  483.  
  484.       //var override = document.createAttribute("data-override");
  485.       btnArmHome.removeAttributeNode(this.override);
  486.  
  487.       var arm_mode = document.createAttribute("data-call");
  488.       arm_mode.value = 'alarm_arm_home';
  489.       btnArmHome.setAttributeNode(arm_mode);
  490.  
  491.       var arm_mode2 = document.createAttribute("data-call");
  492.       arm_mode2.value = 'alarm_arm_away';
  493.       btnArmAway.setAttributeNode(arm_mode2);
  494.  
  495.       btnArmHome.innerHTML = "<iron-icon icon='mdi:lock-outline'></iron-icon>&nbsp;Home Mode";
  496.       btnArmAway.innerHTML = "<iron-icon icon='mdi:lock'></iron-icon>&nbsp;Away Mode";
  497.  
  498.    },
  499.    entityTapped: function (ev) {
  500.       ev.stopPropagation();
  501.       var entityId = ev.target.getAttribute('data-entity');
  502.       this.fire('hass-more-info', { entityId: entityId });
  503.    },
  504.    // Observer: polymer gaurantees that this won't be called util hass and panel are both defined
  505.    onPanelUpdate: function(hass, panel) {
  506.       //console.log('onPanelUpdate');
  507.       this.alarm = hass.states[panel.config.alarmid];
  508.       //console.log('Alarm is ', this.alarm);
  509.    },
  510. });
  511. </script>

 

Mostrar Código

Y guardamos.

Al final de la entrada dejo un .zip con las dependencias JS que habrá que dejar en el directorio www/lib/ de nuestra instalación HA. Si quieres puedes cargarlas con wget así:

bash

  1. cd /home/homeassistant/.homeassistant/www
  2.  
  3. mkdir lib
  4.  
  5. cd lib
  6.  
  7. wget http://domology.es/wp-content/uploads/2018/01/countdown360.js
  8.  
  9. wget http://domology.es/wp-content/uploads/2018/01/jquery-3.2.1.min.js

 

Volvemos un directorio atrás

cd ../

y entramos en el directorio custom_components (si no existe lo creas) la ruta completa sería /home/homeassistant/.homeassistant/custom_components

cd custom_components

crear el directorio alarm_control_panel

mkdir alarm_control_panel

y crear el fichero bwalarm.py

nano bwalarm.py

python

  1. """
  2. My take on the manual alarm control panel
  3. """
  4. import asyncio
  5. import datetime
  6. import logging
  7. import enum
  8. import re
  9. import voluptuous as vol
  10. from operator import attrgetter
  11.  
  12. from homeassistant.const import (
  13. STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
  14. STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, CONF_PLATFORM, CONF_NAME,
  15. CONF_CODE, CONF_PENDING_TIME, CONF_TRIGGER_TIME, CONF_DISARM_AFTER_TRIGGER,
  16. EVENT_STATE_CHANGED, EVENT_TIME_CHANGED,
  17. STATE_ON)
  18. from homeassistant.util.dt import utcnow as now
  19. from homeassistant.helpers.event import async_track_point_in_time
  20. import homeassistant.components.alarm_control_panel as alarm
  21. import homeassistant.components.switch as switch
  22. import homeassistant.helpers.config_validation as cv
  23.  
  24. STATE_TRUE = 'true'
  25. STATE_UNLOCKED = 'unlocked'
  26. STATE_OPEN = 'open'
  27. STATE_DETECTED = 'detected'
  28.  
  29. CONF_PANIC_CODE = 'panic_code'
  30. CONF_IMMEDIATE = 'immediate'
  31. CONF_DELAYED = 'delayed'
  32. CONF_IGNORE = 'homemodeignore'
  33. CONF_NOTATHOME = 'notathome'
  34. CONF_OVERRIDE = 'override'
  35. CONF_PERIMETER_MODE = 'perimeter_mode'
  36. CONF_PERIMETER = 'perimeter'
  37. CONF_ALARM = 'alarm'
  38. CONF_WARNING = 'warning'
  39.  
  40. CONF_WARNING_COLOUR = 'warning_colour'
  41. CONF_PENDING_COLOUR = 'pending_colour'
  42. CONF_DISARMED_COLOUR = 'disarmed_colour'
  43. CONF_TRIGGERED_COLOUR = 'triggered_colour'
  44. CONF_ARMED_AWAY_COLOUR = 'armed_away_colour'
  45. CONF_ARMED_HOME_COLOUR = 'armed_home_colour'
  46.  
  47. CONF_CLOCK = 'clock'
  48. CONF_WEATHER = 'weather'
  49.  
  50. # Add a new state for the time after an delayed sensor and an actual alarm
  51. STATE_ALARM_WARNING = 'warning'
  52. STATE_ALARM_ARMED_PERIMETER = 'armed_perimeter'
  53. class Events(enum.Enum):
  54. ImmediateTrip = 1
  55. DelayedTrip = 2
  56. ArmHome = 3
  57. ArmAway = 4
  58. Timeout = 5
  59. Disarm = 6
  60. Trigger = 7
  61. ArmPerimeter = 8
  62.  
  63. PLATFORM_SCHEMA = vol.Schema({
  64. vol.Required(CONF_PLATFORM): 'bwalarm',
  65. vol.Required(CONF_NAME): cv.string,
  66. vol.Required(CONF_PENDING_TIME): vol.All(vol.Coerce(int), vol.Range(min=0)),
  67. vol.Required(CONF_TRIGGER_TIME): vol.All(vol.Coerce(int), vol.Range(min=1)),
  68. vol.Required(CONF_ALARM): cv.entity_id, # switch/group to turn on when alarming
  69. vol.Required(CONF_WARNING): cv.entity_id, # switch/group to turn on when warning
  70. vol.Optional(CONF_CODE): cv.string,
  71. vol.Optional(CONF_PANIC_CODE): cv.string,
  72. vol.Optional(CONF_IMMEDIATE): cv.entity_ids, # things that cause an immediate alarm
  73. vol.Optional(CONF_DELAYED): cv.entity_ids, # things that allow a delay before alarm
  74. vol.Optional(CONF_IGNORE): cv.entity_ids, # things that we ignore when at home
  75. vol.Optional(CONF_NOTATHOME): cv.entity_ids, # things that we ignore when at home BACKWARDS COMPAT
  76. vol.Optional(CONF_OVERRIDE): cv.entity_ids, # sensors that can be ignored if open when trying to set alarm in away mode
  77. vol.Optional(CONF_PERIMETER_MODE): cv.boolean, # Enable perimeter mode?
  78. vol.Optional(CONF_PERIMETER): cv.entity_ids, # things monitored under perimeter mode
  79. vol.Optional(CONF_WARNING_COLOUR): cv.string, # Custom colour of warning display
  80. vol.Optional(CONF_PENDING_COLOUR): cv.string, # Custom colour of pending display
  81. vol.Optional(CONF_DISARMED_COLOUR): cv.string, # Custom colour of disarmed display
  82. vol.Optional(CONF_TRIGGERED_COLOUR): cv.string, # Custom colour of triggered display
  83. vol.Optional(CONF_ARMED_AWAY_COLOUR): cv.string, # Custom colour of armed away display
  84. vol.Optional(CONF_ARMED_HOME_COLOUR): cv.string, # Custom colour of armed home display
  85. vol.Optional(CONF_CLOCK): cv.boolean, # DIsplay clock on panel
  86. vol.Optional(CONF_WEATHER): cv.boolean # DIsplay weather on panel
  87. })
  88.  
  89. _LOGGER = logging.getLogger(__name__)
  90.  
  91. @asyncio.coroutine
  92. def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
  93. alarm = BWAlarm(hass, config)
  94. hass.bus.async_listen(EVENT_STATE_CHANGED, alarm.state_change_listener)
  95. hass.bus.async_listen(EVENT_TIME_CHANGED, alarm.time_change_listener)
  96. async_add_devices([alarm])
  97.  
  98. class BWAlarm(alarm.AlarmControlPanel):
  99.  
  100. def __init__(self, hass, config):
  101. """ Initalize the alarm system """
  102. self._hass = hass
  103. self._name = config[CONF_NAME]
  104. self._immediate = set(config.get(CONF_IMMEDIATE, []))
  105. self._delayed = set(config.get(CONF_DELAYED, []))
  106. self._ignore = set(config.get(CONF_IGNORE, []) if config.get(CONF_IGNORE, []) != [] else config.get(CONF_NOTATHOME, []))
  107. self._override = set(config.get(CONF_OVERRIDE, []))
  108. self._perimeter_mode = config.get(CONF_PERIMETER_MODE, False)
  109. self._perimeter = set(config.get(CONF_PERIMETER, []))
  110. self._allsensors = self._immediate | self._delayed | self._ignore
  111. #self._allsensors = self._allinputs
  112. self._alarm = config[CONF_ALARM]
  113. self._warning = config[CONF_WARNING]
  114. self._code = config[CONF_CODE] if config[CONF_CODE] else None
  115. self._panic_code = config.get(CONF_PANIC_CODE, None)
  116.  
  117. self._countdown_time = config[CONF_PENDING_TIME]
  118. self._pending_time = datetime.timedelta(seconds=config[CONF_PENDING_TIME])
  119. self._trigger_time = datetime.timedelta(seconds=config[CONF_TRIGGER_TIME])
  120.  
  121. self._lasttrigger = ""
  122. self._state = STATE_ALARM_DISARMED
  123. self._returnto = STATE_ALARM_DISARMED
  124. self._timeoutat = None
  125.  
  126. self._warning_colour = config.get(CONF_WARNING_COLOUR, 'orange')
  127. self._pending_colour = config.get(CONF_PENDING_COLOUR, 'orange')
  128. self._disarmed_colour = config.get(CONF_DISARMED_COLOUR, '#03A9F4')
  129. self._triggered_colour = config.get(CONF_TRIGGERED_COLOUR, 'red')
  130. self._armed_away_colour = config.get(CONF_ARMED_AWAY_COLOUR, 'black')
  131. self._armed_home_colour = config.get(CONF_ARMED_HOME_COLOUR, 'black')
  132.  
  133. self._panic_mode = "deactivated"
  134.  
  135. self._clock = config.get(CONF_CLOCK, False)
  136. self._weather = config.get(CONF_WEATHER, False)
  137.  
  138. self.clearsignals()
  139.  
  140. ### Alarm properties
  141. @property
  142. def should_poll(self) -&gt; bool: return False
  143. @property
  144. def name(self) -&gt; str: return self._name
  145. @property
  146. def changed_by(self) -&gt; str: return self._lasttrigger
  147. @property
  148. def state(self) -&gt; str: return self._state
  149. @property
  150. def device_state_attributes(self):
  151. return {
  152. 'immediate': sorted(list(self.immediate)),
  153. 'delayed': sorted(list(self.delayed)),
  154. 'override': sorted(list(self._override)),
  155. 'ignored': sorted(list(self.ignored)),
  156. 'allsensors': sorted(list(self._allsensors)),
  157. 'perimeter_mode': self._perimeter_mode,
  158. 'perimeter': sorted(list(self._perimeter)),
  159. 'changedby': self.changed_by,
  160. 'warning_colour': self._warning_colour,
  161. 'pending_colour': self._pending_colour,
  162. 'disarmed_colour': self._disarmed_colour,
  163. 'triggered_colour': self._triggered_colour,
  164. 'armed_home_colour': self._armed_home_colour,
  165. 'armed_away_colour': self._armed_away_colour,
  166. 'panic_mode': self._panic_mode,
  167. 'countdown_time': self._countdown_time,
  168. 'clock': self._clock,
  169. 'weather': self._weather
  170. }
  171.  
  172. ### Actions from the outside world that affect us, turn into enum events for internal processing
  173. def time_change_listener(self, eventignored):
  174. """ I just treat the time events as a periodic check, its simpler then (re-/un-)registration """
  175. if self._timeoutat is not None:
  176. if now() &gt; self._timeoutat:
  177. self._timeoutat = None
  178. self.process_event(Events.Timeout)
  179.  
  180. def state_change_listener(self, event):
  181. """ Something changed, we only care about things turning on at this point """
  182. new = event.data.get('new_state', None)
  183. if new is None:
  184. return
  185. if new.state.lower() == STATE_ON or new.state.lower() == STATE_TRUE or new.state.lower() == STATE_UNLOCKED or new.state.lower() == STATE_OPEN or new.state.lower() == STATE_DETECTED:
  186. eid = event.data['entity_id']
  187. if eid in self.immediate:
  188. self._lasttrigger = eid
  189. self.process_event(Events.ImmediateTrip)
  190. elif eid in self.delayed:
  191. self._lasttrigger = eid
  192. self.process_event(Events.DelayedTrip)
  193.  
  194. @property
  195. def code_format(self):
  196. """One or more characters."""
  197. return None if self._code is None else '.+'
  198.  
  199. def alarm_disarm(self, code=None):
  200.  
  201. #If the provided code matches the panic alarm then deactivate the alarm but set the state of the panic mode to active.
  202. if self._validate_panic_code(code):
  203. self.process_event(Events.Disarm)
  204. self._panic_mode = "ACTIVE"
  205. # Let HA know that something changed
  206. self.schedule_update_ha_state()
  207. return
  208.  
  209. if not self._validate_code(code, STATE_ALARM_DISARMED):
  210. return
  211. self.process_event(Events.Disarm)
  212.  
  213. def alarm_arm_home(self, code):
  214. self.process_event(Events.ArmHome)
  215.  
  216. def alarm_arm_away(self, code=None):
  217. self.process_event(Events.ArmAway)
  218.  
  219. def alarm_arm_night(self, code=None):
  220. self.process_event(Events.ArmPerimeter)
  221.  
  222. def alarm_trigger(self, code=None):
  223. self.process_event(Events.Trigger)
  224.  
  225. ### Internal processing
  226. def setsignals(self, alarmMode):
  227. """ Figure out what to sense and how """
  228. if alarmMode == Events.ArmHome or alarmMode == Events.ArmAway:
  229. self.immediate = self._immediate.copy()
  230. self.delayed = self._delayed.copy()
  231. if alarmMode == Events.ArmHome:
  232. self.immediate -= self._ignore
  233. self.delayed -= self._ignore
  234. if alarmMode == Events.ArmPerimeter:
  235. self.immediate = self._perimeter.copy()
  236. self.ignored = self._allsensors - (self.immediate | self.delayed)
  237.  
  238. def clearsignals(self):
  239. """ Clear all our signals, we aren't listening anymore """
  240. self._panic_mode = "deactivated"
  241. self.immediate = set()
  242. self.delayed = set()
  243. self.ignored = self._allsensors.copy()
  244.  
  245. def process_event(self, event):
  246. old = self._state
  247.  
  248. # Update state if applicable
  249. if event == Events.Disarm:
  250. self._state = STATE_ALARM_DISARMED
  251. elif event == Events.Trigger:
  252. self._state = STATE_ALARM_TRIGGERED
  253. elif old == STATE_ALARM_DISARMED:
  254. if event == Events.ArmHome: self._state = STATE_ALARM_ARMED_HOME
  255. elif event == Events.ArmAway: self._state = STATE_ALARM_PENDING
  256. elif event == Events.ArmPerimeter: self._state = STATE_ALARM_ARMED_PERIMETER
  257. elif old == STATE_ALARM_PENDING:
  258. if event == Events.Timeout: self._state = STATE_ALARM_ARMED_AWAY
  259. elif old == STATE_ALARM_ARMED_HOME or \
  260. old == STATE_ALARM_ARMED_AWAY or \
  261. old == STATE_ALARM_ARMED_PERIMETER:
  262. if event == Events.ImmediateTrip: self._state = STATE_ALARM_TRIGGERED
  263. elif event == Events.DelayedTrip: self._state = STATE_ALARM_WARNING
  264. elif old == STATE_ALARM_WARNING:
  265. if event == Events.Timeout: self._state = STATE_ALARM_TRIGGERED
  266. elif old == STATE_ALARM_TRIGGERED:
  267. if event == Events.Timeout: self._state = self._returnto
  268.  
  269. new = self._state
  270. if old != new:
  271. _LOGGER.debug("Alarm changing from {} to {}".format(old, new))
  272. # Things to do on entering state
  273. if new == STATE_ALARM_WARNING:
  274. _LOGGER.debug("Turning on warning")
  275. switch.turn_on(self._hass, self._warning)
  276. self._timeoutat = now() + self._pending_time
  277. elif new == STATE_ALARM_TRIGGERED:
  278. _LOGGER.debug("Turning on alarm")
  279. switch.turn_on(self._hass, self._alarm)
  280. self._timeoutat = now() + self._trigger_time
  281. elif new == STATE_ALARM_PENDING:
  282. _LOGGER.debug("Pending user leaving house")
  283. switch.turn_on(self._hass, self._warning)
  284. self._timeoutat = now() + self._pending_time
  285. self._returnto = STATE_ALARM_ARMED_AWAY
  286. self.setsignals(Events.ArmAway)
  287. elif new == STATE_ALARM_ARMED_HOME:
  288. self._returnto = new
  289. self.setsignals(Events.ArmHome)
  290. elif new == STATE_ALARM_ARMED_AWAY:
  291. self._returnto = new
  292. self.setsignals(Events.ArmAway)
  293. elif new == STATE_ALARM_ARMED_PERIMETER:
  294. self._returnto = new
  295. self.setsignals(Events.ArmPerimeter)
  296. elif new == STATE_ALARM_DISARMED:
  297. self._returnto = new
  298. self.clearsignals()
  299.  
  300. # Things to do on leaving state
  301. if old == STATE_ALARM_WARNING or old == STATE_ALARM_PENDING:
  302. _LOGGER.debug("Turning off warning")
  303. switch.turn_off(self._hass, self._warning)
  304. elif old == STATE_ALARM_TRIGGERED:
  305. _LOGGER.debug("Turning off alarm")
  306. switch.turn_off(self._hass, self._alarm)
  307.  
  308. # Let HA know that something changed
  309. self.schedule_update_ha_state()
  310.  
  311. def _validate_code(self, code, state):
  312. """Validate given code."""
  313. check = self._code is None or code == self._code
  314. if not check:
  315. _LOGGER.debug("Invalid code given for %s", state)
  316. return check
  317.  
  318. def _validate_panic_code(self, code):
  319. """Validate given code."""
  320. check = code == self._panic_code
  321. if check:
  322. _LOGGER.warning("[ALARM] PANIC MODE ACTIVATED!!!")
  323. return check

Mostrar Código

Guardamos y listo, ya tenemos todos los ficheros necesarios, ahora simplemente faltaria crear la automatizacion y subir las voces al Gateway de Xiaomi mediante la APP Mi Home.

Los sonidos personalizados del GW de Xiaomi empiezan por el ID 10004, debereis ir llamandolos en el script de automatizacion segun el orden de vuestro GW.

Aqui os dejo mi Automation de ejemplo.

yaml

  1. - id: alarm_armed_away
  2. alias: '[Alarm] Away Mode Armed'
  3. trigger:
  4. - platform: state
  5. entity_id: alarm_control_panel.house
  6. to: 'armed_away'
  7. action:
  8. - service: xiaomi_aqara.play_ringtone
  9. data:
  10. gw_mac: 34:ce:00:88:xx:xx
  11. ringtone_id: 10010 # Alarma Activada.
  12. ringtone_vol: 100
  13. - service: switch.turn_on
  14. entity_id: switch.cerrarentrada
  15. - service: notify.telegram
  16. data:
  17. message: "* Alarma Activada*. Adios :)"
  18. - service: climate.set_temperature
  19. data:
  20. entity_id: climate.aa_fujitsu
  21. temperature: 23
  22. operation_mode: Apagado
  23. - service: input_boolean.turn_on
  24. data:
  25. entity_id: input_boolean.alarma_casa
  26.  
  27. - id: alarm_armed_home
  28. alias: '[Alarm] Home Mode Armed'
  29. trigger:
  30. - platform: state
  31. entity_id: alarm_control_panel.house
  32. to: 'armed_home'
  33. action:
  34. - service: xiaomi_aqara.play_ringtone
  35. data:
  36. gw_mac: 34:ce:00:88:xx:xx
  37. ringtone_id: 10011 # Modo casa activado. Buenas noches
  38. ringtone_vol: 50
  39. - service: notify.telegram
  40. data:
  41. message: "*Modo noche activado*. Buenas noches."
  42. - service: input_boolean.turn_on
  43. data:
  44. entity_id: input_boolean.alarma_casa
  45.  
  46. - id: alarm_arming_away
  47. alias: '[Alarm] Away Mode Arming'
  48. trigger:
  49. - platform: state
  50. entity_id: alarm_control_panel.house
  51. to: 'pending'
  52. action:
  53. - service: xiaomi_aqara.play_ringtone
  54. data:
  55. gw_mac: 34:ce:00:88:xx:xx
  56. ringtone_id: 10012 # Activando alarma, revisa que puertas y ventanas esten cerradas
  57. ringtone_vol: 100
  58. - delay:
  59. seconds: 8
  60. - service: xiaomi_aqara.play_ringtone
  61. data:
  62. gw_mac: 34:ce:00:88:xx:xx
  63. ringtone_id: 10017 # Contador 22 Segundos
  64. ringtone_vol: 100
  65.  
  66. - id: alarm_disarmed
  67. alias: '[Alarm] Disarmed'
  68. trigger:
  69. - platform: state
  70. entity_id: alarm_control_panel.house
  71. to: 'disarmed'
  72. action:
  73. - service: xiaomi_aqara.play_ringtone
  74. data:
  75. gw_mac: 34:ce:00:88:xx:xx
  76. ringtone_id: 10014 # Alarma desactivada
  77. ringtone_vol: 100
  78. - service: input_boolean.turn_off
  79. data:
  80. entity_id: input_boolean.alarma_casa
  81. - service: notify.telegram
  82. data:
  83. message: "Alarma *desactivada*"
  84. - service: automation.turn_off
  85. entity_id: automation.sirena1
  86. - service: automation.turn_off
  87. entity_id: automation.sirena2
  88. - service: automation.turn_off
  89. entity_id: automation.sirena3
  90.  
  91. - id: alarm_triggered
  92. alias: '[Alarm] Triggered'
  93. trigger:
  94. - platform: state
  95. entity_id: alarm_control_panel.house
  96. to: 'triggered'
  97. action:
  98. - service: automation.turn_on
  99. entity_id: automation.sirena1
  100. # - service: switch.turn_on
  101. # entity_id: switch.siren_switch
  102. - service: notify.telegram
  103. data:
  104. message: 'HA SALTADO LA ALARMA!!! {{ states[states.alarm_control_panel.house.attributes.changed_by.split(".")[0]][ states.alarm_control_panel.house.attributes.changed_by.split(".")[1]].name }}'
  105.  
  106. - id: alarm_warning
  107. alias: '[Alarm] Warning'
  108. trigger:
  109. - platform: state
  110. entity_id: alarm_control_panel.house
  111. to: 'warning'
  112. action:
  113. - service: notify.telegram
  114. data:
  115. message: 'ALARM Warning {{ states[states.alarm_control_panel.house.attributes.changed_by.split(".")[0]][ states.alarm_control_panel.house.attributes.changed_by.split(".")[1]].name }}'
  116. - service: xiaomi_aqara.play_ringtone
  117. data:
  118. gw_mac: 34:ce:00:88:xx:xx
  119. ringtone_id: 10013 # Se ha disparado la alarma, por favor desactivar.
  120. ringtone_vol: 100
  121.  
  122. ####################################################
  123. ################# P U L S A D O R #################
  124. ####################################################
  125. - alias: "Alarma Casa on"
  126. trigger:
  127. - platform: state
  128. entity_id: input_boolean.alarma_casa
  129. to: 'on'
  130. action:
  131. - service: alarm_control_panel.alarm_arm_away
  132. data:
  133. entity_id: alarm_control_panel.house
  134. # code: !secret alarm_code
  135.  
  136. - alias: "Alarma Casa off"
  137. trigger:
  138. - platform: state
  139. entity_id: input_boolean.alarma_casa
  140. to: 'off'
  141. action:
  142. - service: alarm_control_panel.alarm_disarm
  143. data:
  144. entity_id: alarm_control_panel.house
  145. code: !secret alarm_code
  146.  
  147. ####################################################
  148. ################# S I R E N A ######################
  149. ####################################################
  150. - alias: "Sirena1"
  151. initial_state: False
  152. hide_entity: False
  153. trigger:
  154. - platform: state
  155. entity_id: automation.sirena1
  156. from: 'off'
  157. to: 'on'
  158. condition:
  159. - condition: state
  160. entity_id: alarm_control_panel.house
  161. state: 'triggered'
  162. action:
  163. - service: automation.turn_off
  164. entity_id: automation.sirena3
  165. - service: xiaomi_aqara.play_ringtone
  166. data:
  167. gw_mac: 34:ce:00:88:xx:xx
  168. ringtone_id: 2
  169. ringtone_vol: 100
  170. - delay:
  171. seconds: 4
  172. - service: automation.turn_on
  173. entity_id: automation.sirena2
  174.  
  175. - alias: "Sirena2"
  176. initial_state: False
  177. hide_entity: False
  178. trigger:
  179. - platform: state
  180. entity_id: automation.sirena2
  181. from: 'off'
  182. to: 'on'
  183. condition:
  184. - condition: state
  185. entity_id: alarm_control_panel.house
  186. state: 'triggered'
  187. action:
  188. - service: automation.turn_off
  189. entity_id: automation.sirena1
  190. - service: xiaomi_aqara.play_ringtone
  191. data:
  192. gw_mac: 34:ce:00:88:xx:xx
  193. ringtone_id: 2
  194. ringtone_vol: 100
  195. - delay:
  196. seconds: 4
  197. - service: automation.turn_on
  198. entity_id: automation.sirena3
  199.  
  200. - alias: "Sirena3"
  201. initial_state: False
  202. hide_entity: False
  203. trigger:
  204. - platform: state
  205. entity_id: automation.sirena3
  206. from: 'off'
  207. to: 'on'
  208. condition:
  209. - condition: state
  210. entity_id: alarm_control_panel.house
  211. state: 'triggered'
  212. action:
  213. - service: automation.turn_off
  214. entity_id: automation.sirena2
  215. - service: xiaomi_aqara.play_ringtone
  216. data:
  217. gw_mac: 34:ce:00:88:xx:xx
  218. ringtone_id: 2
  219. ringtone_vol: 100
  220. - delay:
  221. seconds: 4
  222. - service: automation.turn_on
  223. entity_id: automation.sirena1

Mostrar Código

Por ultimo os dejo un paquete con todos los ficheros y las Voces que tengo generadas para esa automatización.

 

Alarma HASS+XiaomiGW-ESP-ByRubenzori86

 

Si queréis mas información sobre esta alarma podéis visitar al creador de la misma en GitHub.

https://github.com/gazoscalvertos/Hass-Custom-Alarm

También te podría gustar...

4 Respuestas

  1. nasked dice:

    Para el tema de los sonidos yo he gastado Balabolka. Me gusta más como suena la voz de la chica en el Gateway

  2. nasked dice:

    Por cierto… veo que en los iconos de las puertas tienes creo, el que sale la puerta cerrada y cuando se abre el icono de la puerta abierta. A ver si alguién me dice como se consigue hacer eso..Estaría bien un tutorial para customizar los iconos, iconos que cambian de aspecto o de color. He visto que el foro de HA hay mucha gente preguntanto por eso y lo que he probado a mi no me ha funcionado.

  1. 21 enero, 2018

    […] Activar la alarma o mensajería al móvil al salir de casa, por ejemplo, tenerlo más o menos oculto y al salir de […]

  2. 22 enero, 2018

    […] relacionadas con Home-Assistant, habrás visto como crear desde un bloqueador de anuncios hasta una alarma o alguna otra filigrana, pero quizá nos hayamos saltado lo mas […]

Deja un comentario