Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

circle-sensor-card.js 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import {
  2. LitElement, html
  3. } from 'https://unpkg.com/@polymer/lit-element@^0.5.2/lit-element.js?module';
  4. class CircleSensorCard extends LitElement {
  5. static get properties() {
  6. return {
  7. hass: Object,
  8. config: Object,
  9. state: Object,
  10. dashArray: String
  11. }
  12. }
  13. _render({ state, dashArray, config }) {
  14. return html`
  15. <style>
  16. :host {
  17. cursor: pointer;
  18. }
  19. .container {
  20. position: relative;
  21. height: 100%;
  22. display: flex;
  23. flex-direction: column;
  24. }
  25. .labelContainer {
  26. position: absolute;
  27. top: 0;
  28. left: 0;
  29. width: 100%;
  30. height: 100%;
  31. display: flex;
  32. flex-direction: column;
  33. align-items: center;
  34. justify-content: center;
  35. }
  36. #label {
  37. display: flex;
  38. line-height: 1;
  39. }
  40. #label.bold {
  41. font-weight: bold;
  42. }
  43. #label, #name {
  44. margin: 1% 0;
  45. }
  46. .text, #name {
  47. font-size: 100%;
  48. }
  49. .unit {
  50. font-size: 75%;
  51. }
  52. </style>
  53. <div class="container" id="container" on-click="${() => this._click()}">
  54. <svg viewbox="0 0 200 200" id="svg">
  55. <circle id="circle" cx="50%" cy="50%" r="45%"
  56. fill$="${config.fill || 'rgba(255, 255, 255, .75)'}"
  57. stroke$="${config.stroke_color || '#03a9f4'}"
  58. stroke-dasharray$="${dashArray}"
  59. stroke-width$="${config.stroke_width || 6}"
  60. transform="rotate(-90 100 100)"/>
  61. </svg>
  62. <span class="labelContainer">
  63. ${config.name != null ? html`<span id="name">${config.name}</span>` : ''}
  64. <span id="label" class$="${!!config.name ? 'bold' : ''}">
  65. <span class="text">
  66. ${config.attribute ? state.attributes[config.attribute] : state.state}
  67. </span>
  68. <span class="unit">
  69. ${config.show_max
  70. ? html`&nbsp/ ${config.attribute_max ? state.attributes[config.attribute_max] : config.max}`
  71. : (config.units ? config.units : state.attributes.unit_of_measurement)}
  72. </span>
  73. </span>
  74. </span>
  75. </div>
  76. `;
  77. }
  78. _createRoot() {
  79. const shadow = this.attachShadow({ mode: 'open' })
  80. if (!this.config.show_card) {
  81. return shadow;
  82. }
  83. const card = document.createElement('ha-card');
  84. shadow.appendChild(card);
  85. return card;
  86. }
  87. _didRender() {
  88. this.circle = this._root.querySelector('#circle');
  89. if (this.config) {
  90. this._updateConfig();
  91. }
  92. }
  93. setConfig(config) {
  94. if (!config.entity) {
  95. throw Error('No entity defined')
  96. }
  97. this.config = config;
  98. if (this.circle) {
  99. this._updateConfig();
  100. }
  101. }
  102. getCardSize() {
  103. return 3;
  104. }
  105. _updateConfig() {
  106. const container = this._root.querySelector('.labelContainer');
  107. container.style.color = 'var(--primary-text-color)';
  108. if (this.config.font_style) {
  109. Object.keys(this.config.font_style).forEach((prop) => {
  110. container.style.setProperty(prop, this.config.font_style[prop]);
  111. });
  112. }
  113. }
  114. set hass(hass) {
  115. this.state = hass.states[this.config.entity];
  116. if (this.config.attribute) {
  117. if (!this.state.attributes[this.config.attribute] ||
  118. isNaN(this.state.attributes[this.config.attribute])) {
  119. console.error(`Attribute [${this.config.attribute}] is not a number`);
  120. return;
  121. }
  122. } else {
  123. if (!this.state || isNaN(this.state.state)) {
  124. console.error(`State is not a number`);
  125. return;
  126. }
  127. }
  128. const state = this.config.attribute
  129. ? this.state.attributes[this.config.attribute]
  130. : this.state.state;
  131. const r = 200 * .45;
  132. const min = this.config.min || 0;
  133. const max = this.config.attribute_max
  134. ? this.state.attributes[this.config.attribute_max]
  135. : (this.config.max || 100);
  136. const val = this._calculateValueBetween(min, max, state);
  137. const score = val * 2 * Math.PI * r;
  138. const total = 10 * r;
  139. this.dashArray = `${score} ${total}`;
  140. let colorStops = {};
  141. colorStops[min] = this.config.stroke_color || '#03a9f4';
  142. if (this.config.color_stops) {
  143. Object.keys(this.config.color_stops).forEach((key) => {
  144. colorStops[key] = this.config.color_stops[key];
  145. });
  146. }
  147. if (this.circle) {
  148. const stroke = this._calculateStrokeColor(state, colorStops);
  149. this.circle.setAttribute('stroke', stroke);
  150. }
  151. }
  152. _click() {
  153. this._fire('hass-more-info', { entityId: this.config.entity });
  154. }
  155. _calculateStrokeColor(state, stops) {
  156. const sortedStops = Object.keys(stops).map(n => Number(n)).sort((a, b) => a - b);
  157. let start, end, val;
  158. const l = sortedStops.length;
  159. if (state <= sortedStops[0]) {
  160. return stops[sortedStops[0]];
  161. } else if (state >= sortedStops[l - 1]) {
  162. return stops[sortedStops[l - 1]];
  163. } else {
  164. for (let i = 0; i < l - 1; i++) {
  165. const s1 = sortedStops[i];
  166. const s2 = sortedStops[i + 1];
  167. if (state >= s1 && state < s2) {
  168. [start, end] = [stops[s1], stops[s2]];
  169. if (!this.config.gradient) {
  170. return start;
  171. }
  172. val = this._calculateValueBetween(s1, s2, state);
  173. break;
  174. }
  175. }
  176. }
  177. return this._getGradientValue(start, end, val);
  178. }
  179. _calculateValueBetween(start, end, val) {
  180. return (val - start) / (end - start);
  181. }
  182. _getGradientValue(colorA, colorB, val) {
  183. const v1 = 1 - val;
  184. const v2 = val;
  185. const decA = this._hexColorToDecimal(colorA);
  186. const decB = this._hexColorToDecimal(colorB);
  187. const rDec = Math.floor((decA[0] * v1) + (decB[0] * v2));
  188. const gDec = Math.floor((decA[1] * v1) + (decB[1] * v2));
  189. const bDec = Math.floor((decA[2] * v1) + (decB[2] * v2));
  190. const rHex = this._padZero(rDec.toString(16));
  191. const gHex = this._padZero(gDec.toString(16));
  192. const bHex = this._padZero(bDec.toString(16));
  193. return `#${rHex}${gHex}${bHex}`;
  194. }
  195. _hexColorToDecimal(color) {
  196. let c = color.substr(1);
  197. if (c.length === 3) {
  198. c = `${c[0]}${c[0]}${c[1]}${c[1]}${c[2]}${c[2]}`;
  199. }
  200. const [r, g, b] = c.match(/.{2}/g);
  201. return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
  202. }
  203. _padZero(val) {
  204. if (val.length < 2) {
  205. val = `0${val}`;
  206. }
  207. return val.substr(0, 2);
  208. }
  209. _fire(type, detail) {
  210. const event = new Event(type, {
  211. bubbles: true,
  212. cancelable: false,
  213. composed: true
  214. });
  215. event.detail = detail || {};
  216. this.shadowRoot.dispatchEvent(event);
  217. return event;
  218. }
  219. }
  220. customElements.define('circle-sensor-card', CircleSensorCard);