[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# editorconfig.org\n\nroot = true\n\n\n[*]\n\n# Change these settings to your own preference\nindent_style = space\nindent_size = 2\n\n# We recommend you to keep these unchanged\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.json]\nindent_size = 2\n\n[*.{html,js,md}]\nblock_comment_start = /**\nblock_comment = *\nblock_comment_end = */\n"
  },
  {
    "path": ".github/workflows/validate.yaml",
    "content": "name: Validate\n\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: \"0 0 * * *\"\n\njobs:\n  validate:\n    runs-on: \"ubuntu-latest\"\n    steps:\n      - uses: \"actions/checkout@v2\"\n      - name: HACS validation\n        uses: \"hacs/integration/action@main\"\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          CATEGORY: \"plugin\"\n"
  },
  {
    "path": ".gitignore",
    "content": "## editors\n/.idea\n/.vscode\n\n## system files\n.DS_Store\n\n## npm\n/node_modules/\n/npm-debug.log\n\n## testing\n/coverage/\n\n## temp folders\n/.tmp/\n\n# build\n/_site/\n/dist/\n/out-tsc/\n/build\nstorybook-static\n\n*.d.ts\nLICENSE"
  },
  {
    "path": "LICENSE-2.0.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# tesla-style-solar-power-card\n\n> **⚠ WARNING: BREAKING CONFIG CHANGE**\n\n> **You have to define the FLOWS AGAIN!!**  \n> Without defining each flow no line will show, read the usage part carefully (the bubbles can be clickable but this is optional)\n\nThis is a [home-assistant](home-assistant.io) card for solar installations. It provides a tesla style graphic to see the flows of energy ((k)W).\n\n### Table of contents\n* [Concept](#concept)\n* [HACS-Installation](#hacs-installation)  \n* [Usage](#usage)\n* [Tesla Powerwall Usage](#tesla-powerwall-usage)\n* [Contributing](#contributing)\n\n\n![tesla-style-card-animated-gif](https://github.com/reptilex/tesla-style-solar-power-card/blob/master/tesla-style-card-animation.gif)\n\n\n## Concept\nI have tried to make it as generic as possible, for now there are 6 bubbles with 4 main icons and 2 extra appliances. The Four main icon values are controlled by the sum of the flows from and to them:\n1. Grid\n2. Generation (usually solar)\n3. House \n4. Battery\n\nThe two optional appliances can be any consumer in the house, they are attached to the house. These two are controlled directly by their consumption. Meaning no flow sum is done.\n\n* appliance1_consumption_entity (car/heater ...)\n* appliance2_consumption_entity (car2/oven ...)\n\nThe bubbles/icons can be configured to have an entity when clicked, but the numbers are calculated from the flows. You can show an extra entity text/value on the top part of the bubble.\n\nThere are 7 main flows and 2 appliance flows.  The main flows are:\n* generation_to_grid_entity\n* generation_to_battery_entity\n* generation_to_house_entity\n* grid_to_battery_entity\n* grid_to_house_entity\n* battery_to_grid_entity\n* battery_to_house_entity\n\nYou need at least one, the placement of the main bubbles is fixed for now. Some will substract the value from one bubble and will add value to another bubble. For example:\n\nbattery_to_house will substract from the battery bubble/icon and add to house bubble/icon.\n\n## Optional clickable entities\nThe clickable entities can be configured through these entities but are optional:\n\n* grid_entity\n* generation_entity (solar/wind ...)\n* battery_entity\n* house_entity\n\n\nThis card started based on the card from [bessarabov animated consumption card](https://github.com/bessarabov/animated-consumption-card), thanks again for that work. Then was rewritten completely taking [boilerplate card](https://github.com/custom-cards/boilerplate-card) as a starting point but with typescript. I also borrowed a few ideas from [power-wheel-card](https://github.com/gurbyz/power-wheel-card) sadly not yet as many as I would like ;)\n\n## Optional extra entities\n\nOn top of the flows and clickable entity every bubble can have an extra value on top. To define those you need to add a sensor to any of theses entities:\n\n* battery_extra_entity\n* house_extra_entity\n* generation_extra_entity\n* grid_extra_entity\n\nI always have the battery current charge as the battery_extra_entity. In this case the battery icon will also change with the charge.\n\n# HACS-Installation \n1. [install HACS](https://hacs.xyz/docs/installation/installation) if you don't have it yet\n2. When installed go to HACS->Frontend->Explore & add repositories\n3. search for \"tesla style\"\n4. click on the tesla-style-solar-power-card\n5. Install repository\n6. Restart HA\n### Manual Installation (hacs will do all this for you)\n\n1. Add the card js file from the repo under your home assistant config in the www folder (create one if you don't have it yet).\n2. Add a resource under lovelace (you have to enable advanced Mode in your user profile to see the resource tab([see here for this card](https://github.com/reptilex/tesla-style-solar-power-card/blob/master/add-card-resource.png)).\n3. restart home assistant.\n4. add a manual card with the lovelace gui and configure as seen below.\n\n# Usage\n\n\n### Just a grid a house and a line\n\nCurrently I have no minimum configuration, but some combinations might not make sense. I would advice to use the bubbles you want and the flows linked to the one's you are using. The left part of these examples is fixed, change the right part with your own sensors. There are no required entities, though your configuration can show strange results if you leave some combinations out. The sensor can be called whatever you want, they are powermeter sensors in Watt or Kilowatt (choose the same for all, it will create kw from it). __ALL SENSORS NEED TO BE POSITIVE VALUES__\n\nA simple combination example:\n```yml\ntype: 'custom:tesla-style-solar-power-card'\ngrid_consumption_entity: sensor.grid_consumption\nhouse_consumption_entity: sensor.house_consumption\ngrid_to_house_entity: sensor.grid_consumption\n```\nThis will allow you to have two bubbles that are clickable and the flow from grid to house, which will determine the values beneath the icons.\n\n### Complete example with all details\n\n```yml\ntype: 'custom:tesla-style-solar-power-card'\nname: My Flows\n# 7 flows between bubbles\ngrid_to_house_entity: sensor.grid_consumption\ngrid_to_battery_entity: sensor.grid_battery_charge\ngeneration_to_grid_entity: sensor.grid_feed_in\ngeneration_to_battery_entity: sensor.battery_charging\ngeneration_to_house_entity: sensor.solar_consumption\nbattery_to_house_entity: sensor.battery_consumption\nbattery_to_grid_entity: sensor.battery_to_grid\n# extra values to show as text above icons\nbattery_extra_entity: sensor.battery_charge\nhouse_extra_entity: sensor.current_temperature\ngeneration_extra_entity: sensor.percent_cloud_coverage\ngrid_extra_entity: sensor.monthly_feed_in\n# optional appliances with consumption and extra values\nappliance1_consumption_entity: sensor.car_consumption\nappliance1_extra_entity: sensor.car_battery_state_of_charge \nappliance2_consumption_entity: sensor.heating_consumption\nappliance2_extra_entity: sensor.heating_operation\n# optional 4 main bubble icons for clickable entities\ngrid_entity: sensor.grid_consumption\nhouse_entity: sensor.house_consumption\ngeneration_entity: sensor.solar_yield\nbattery_entity: sensor.battery_consumption\n```\nIf you define an extra entity for the battery bubble with the state of charge then the icon will be dynamically replaced with the value of that entity and will override the icon definition above.\n\n\nThere a few configuration variables that change the behaviour:\nHeading:\n```yml\nname: 'My Tesla Power Card!'\n```\n\nOne to force W (Watt) instead of kW, set it to 1 to use it:\n```yml\nshow_w_not_kw: 1\n```\n\nOne to set a different speed for the moving dots, normal speed factor is 0.04 so stay near that number at first, 0.2 is really fast:\n```yml\nspeed_factor: 0.03\n```\n\nOne for the threshold from which W is converted to kW (the example below will change W into kilowatt from 5000 W onwards):\n```yml\nthreshold_in_k: 5\n```\nthreshold_in_k is not compatible with show_w_not_kw, the latter will overrule the threshold_in_k\n\nOne to hide the lines not active to use it, please make sure everything is working before you hide the lines:\n```yml\nhide_inactive_lines: 1\n```\n\nOne to add gaps for the power lines the way the energy panel from ha does it:\n```yml\nshow_gap: true\n```\n\nOne to colour the house bubble depending on the highest flow:\n```yml\nchange_house_bubble_color_with_flow: 1\n```\n\nOne to not show moving circles but an energy flow diagramm (thicker lines when flow is higher):\n```yml\nenergy_flow_diagramm: 1\n```\nThere is a factor to make the lines thicker depending on your flow normaly it's 2:\n```yml\nenergy_flow_diagramm_line_factor: 2\n```\n\nYou can subtract the appliance values from the house value without affecting the line flow:\n```yml\nhouse_without_appliances_values: 1\n```\n\nThen there are 6 icon configuration variables:\n```yml\ngrid_icon: 'mdi:transmission-tower'\ngeneration_icon: 'mdi:solar-panel-large'\nhouse_icon: 'mdi:home'\nbattery_icon: 'mdi:battery'\nappliance1_icon: 'mdi:car-sports'\nappliance2_icon: 'mdi:car-sports'\n```\n\n### templates for missing sensors or for negative sensors\n\nRemember you can create\ntemplate sensors if you are missing one like solar yield out of solar_consumption and grid_feed_in or if you are missing another one like home_consumption. Some inverters have positive and negative values, here all sensors need to be positive values, so create template sensors like:\n```yml\n    battery_consumption:\n        value_template: '{% set batter_cons = sensor.powerwall_battery_now | int %}\n                        {% if batter_cons > 0 %}\n                            {{ batter_cons | int }}\n                        {% else %}\n                            0\n                        {% endif %}'\n        device_class: power\n        unit_of_measurement: W\n``` \n\n# Tesla-Powerwall-Usage \nIn order to use this card with the [Tesla Powerwall integration](https://www.home-assistant.io/integrations/powerwall/) you will need to create some additional sensors first. This card expects an entity with a positive numeric value per line shown on the screen. However the Tesla Powerwall integration creates sensors which go negative or positive depending on whether energy is being consumed from or feed into that particular meter. \n\nFortunately this can be easily fixed with the addition of a few template sensors, the ones you would need to add are shown below. Note that these sensors assume the default names for each entity created by the Tesla Powerwall integration, if you've changed the names of your entities then you'll need to adjust the config accordingly:\n\n```yaml\n# Templates for Actual Powerflow transfer charts (APF - Actual PowerFlow)\n#\n# For the math to add up a new Real House Load must be calculated and used, witch includes\n# the inverter consumption and excludes rounding errors and corrects inaccurate power readings.\n#\n# It never made sense that inbound power sometimes does not equal outbound power. This fixes it!\n#\n# Developed by AviadorLP modified for powerwall by purcell-lab\n# Correctly sets battery2grid & grid2battery flows\n#\ntemplate:\n  - sensor:\n# grid sensor must be negative when importing and positive when exporting\n    - name: APF Grid Entity\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ (0 - states('sensor.powerwall_site_now')|float(0)*1000)|int(0) }}\"\n\n# sensor must always be 0 or positive (i think they always are)\n    - name: APF House Entity\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ (states('sensor.powerwall_load_now')|float(0)*1000)|int(0) }}\"\n\n# sensor must always be 0 or positive (i think they always are)\n    - name: APF Generation Entity\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ (states('sensor.powerwall_solar_now')|float(0)*1000)|int(0) }}\"\n\n# battery sensor must be positive when charging and negative when discharging\n    - name: APF Battery Entity\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ (0 - states('sensor.powerwall_battery_now')|float(0)*1000)|int(0) }}\"\n\n\n\n\n# Required to reduce code later on\n    - name: APF Grid Import\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_grid_entity')|int(default=0) < 0 %}\n          {{ states('sensor.apf_grid_entity')|int(default=0)|abs }}\n        {% else %}\n          0\n        {% endif %}\n\n#   Inverter consumption and power losses due to Inverter transfers and power conversions (AC/DC)\n#   excludes rounding errors made worst by the fact that some inverters round all sensors readings to INT\n#   Occasionally this might be negative probably due to cumulative errors in not so accurate power readings.\n    - name: APF Inverter Power Consumption\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ states('sensor.apf_generation_entity')|int(default=0) - states('sensor.apf_battery_entity')|int(default=0) - states('sensor.apf_house_entity')|int(default=0) - states('sensor.apf_grid_entity')|int(default=0) }}\"\n\n# Real House Load Includes Inverter consumption and transfer conversions and losses and rounding errors.\n# It never made sense that inbound power sometimes does not equal outbound power. This fixes it!\n    - name: APF Real House Load\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: \"{{ states('sensor.apf_house_entity')|int(default=0) + states('sensor.apf_inverter_power_consumption')|int(default=0) }}\"\n      icon: mdi:home-lightning-bolt\n\n    - name: APF Grid2House\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state:  >\n        {% if states('sensor.apf_grid_import')|int(default=0) > states('sensor.apf_real_house_load')|int(default=0) %}\n          {{ states('sensor.apf_real_house_load')|int(default=0) }}\n        {% else %}\n          {{ states('sensor.apf_grid_import')|int(default=0) }}\n        {% endif %}\n\n    - name: APF Grid2Batt\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_grid_import')|int(default=0) > states('sensor.apf_real_house_load')|int(default=0) %}\n          {{ states('sensor.apf_grid_import')|int(default=0) - states('sensor.apf_real_house_load')|int(default=0) }}\n        {% else %}\n          0\n        {% endif %}\n\n    - name: APF Batt2House\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_battery_entity')|int(default=0) < 0 %}\n          {% if states('sensor.apf_battery_entity')|int(default=0)|abs > states('sensor.apf_real_house_load')|int(default=0) %}\n            {{ states('sensor.apf_real_house_load')|int(default=0) }}\n          {% else %}\n            {{ states('sensor.apf_battery_entity')|int(default=0)|abs }}\n          {% endif %}\n        {% else %}\n          0\n        {% endif %}\n\n# This might be called house to grid, and can happen in rare circumstances,\n# like when the inverter is not able to do a precise adjustment of power fast enough\n# or when you want to force a discharge of the battery or something...\n# But it only happens with battery or other power generator users.\n    - name: APF Batt2Grid\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_battery_entity')|int(default=0) < 0 %}\n          {% if states('sensor.apf_battery_entity')|int(default=0)|abs > states('sensor.apf_real_house_load')|int(default=0) %}\n            {{ states('sensor.apf_battery_entity')|int(default=0)|abs - states('sensor.apf_real_house_load')|int(default=0) }}\n          {% else %}\n            0\n          {% endif %}\n        {% else %}\n          0\n        {% endif %}\n\n    - name: APF Solar2Grid\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_grid_entity')|int(default=0) > states('sensor.apf_batt2grid')|int(default=0) %}\n          {{ states('sensor.apf_grid_entity')|int(default=0) - states('sensor.apf_batt2grid')|int(default=0) }}\n        {% else %}\n          0\n        {% endif %}\n\n    - name: APF Solar2House\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_generation_entity')|int(default=0) > 0 and states('sensor.apf_real_house_load')|int(default=0) > states('sensor.apf_batt2house')|int(default=0) + states('sensor.apf_grid_import')|int(default=0) %}\n          {% if states('sensor.apf_generation_entity')|int(default=0) > states('sensor.apf_real_house_load')|int(default=0) - states('sensor.apf_batt2house')|int(default=0) - states('sensor.apf_grid2house')|int(default=0) %}\n            {{ states('sensor.apf_real_house_load')|int(default=0) - states('sensor.apf_batt2house')|int(default=0) - states('sensor.apf_grid2house')|int(default=0) }}\n          {% else %}\n            {{ states('sensor.apf_generation_entity')|int(default=0) }}\n          {% endif %}\n        {% else %}\n          0\n        {% endif %}\n\n    - name: APF Solar2Batt\n      device_class: power\n      state_class: measurement\n      unit_of_measurement: W\n      state: >\n        {% if states('sensor.apf_generation_entity')|int(default=0) > 0 and states('sensor.apf_battery_entity')|int(default=0) > 0 %}\n          {% if states('sensor.apf_battery_entity')|int(default=0) > states('sensor.apf_grid2batt')|int(default=0) %}\n            {% if states('sensor.apf_generation_entity')|int(default=0) - states('sensor.apf_solar2house')|int(default=0) > states('sensor.apf_battery_entity')|int(default=0) - states('sensor.apf_grid2batt')|int(default=0) %}\n              {{ states('sensor.apf_battery_entity')|int(default=0) - states('sensor.apf_grid2batt')|int(default=0) }}\n            {% else %}\n              {{ states('sensor.apf_generation_entity')|int(default=0) - states('sensor.apf_solar2house')|int(default=0) - states('sensor.apf_solar2grid')|int(default=0) }}\n            {% endif %}\n          {% else %}\n            0\n          {% endif %}\n        {% else %}\n          0\n        {% endif %}\n```\nAfter you've included these sensors then you can configure the card like this:\n```yaml\ntype: 'custom:tesla-style-solar-power-card'\n\ngrid_entity: sensor.apf_grid_entity\nhouse_entity: sensor.apf_real_house_load\ngeneration_entity: sensor.apf_generation_entity\nbattery_entity: sensor.apf_battery_entity\n\ngrid_to_house_entity: sensor.apf_grid2house\ngrid_to_battery_entity: sensor.apf_grid2batt\ngeneration_to_grid_entity: sensor.apf_solar2grid\ngeneration_to_battery_entity: sensor.apf_solar2batt\ngeneration_to_house_entity: sensor.apf_solar2house\nbattery_to_house_entity: sensor.apf_batt2house\nbattery_to_grid_entity: sensor.apf_batt2grid\n\nbattery_extra_entity: sensor.powerwall_charge\n```\n\n## Releases\nv0.9\nv0.92\nvbeta1.1.\n\n## Contributing\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n## License\nApache License V 2.0\n"
  },
  {
    "path": "hacs.json",
    "content": "{\n    \"name\": \"Tesla style solar power card\",\n    \"content_in_root\": true,\n    \"filename\": \"tesla-style-solar-power-card.js\",\n    \"render_readme\": true\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"tesla-style-solar-power-card\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Webcomponent tesla-style-solar-power-card following open-wc recommendations\",\n  \"author\": \"tesla-style-solar-power-card\",\n  \"license\": \"Apache-2.0\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.js\",\n  \"scripts\": {\n    \"start\": \"tsc && concurrently -k -r \\\"tsc --watch --preserveWatchOutput\\\" \\\"wds\\\"\",\n    \"build\": \"rimraf dist && tsc && rollup -c rollup.config.js\",\n    \"prepublish\": \"tsc\",\n    \"lint\": \"eslint --ext .ts,.html . --ignore-path .gitignore && prettier \\\"**/*.ts\\\" --check --ignore-path .gitignore\",\n    \"format\": \"eslint --ext .ts,.html . --fix --ignore-path .gitignore && prettier \\\"**/*.ts\\\" --write --ignore-path .gitignore\",\n    \"test\": \"tsc && wtr --coverage\",\n    \"test:watch\": \"tsc && concurrently -k -r \\\"tsc --watch --preserveWatchOutput\\\" \\\"wtr --watch\\\"\"\n  },\n  \"dependencies\": {\n    \"custom-card-helpers\": \"^1.7.0\",\n    \"home-assistant-js-websocket\": \"^5.7.0\",\n    \"lit\": \"^2.2.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/eslint-plugin\": \"^7.12.13\",\n    \"@open-wc/building-rollup\": \"^2.0.1\",\n    \"@open-wc/eslint-config\": \"^4.2.0\",\n    \"@open-wc/testing\": \"^3.1.2\",\n    \"@rollup/plugin-node-resolve\": \"^13.1.3\",\n    \"@types/convert-source-map\": \"^1.5.1\",\n    \"@types/istanbul-reports\": \"^3.0.0\",\n    \"@types/mocha\": \"^9.1.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.14.1\",\n    \"@typescript-eslint/parser\": \"^4.14.1\",\n    \"@web/dev-server\": \"^0.1.5\",\n    \"@web/dev-server-legacy\": \"^0.1.7\",\n    \"@web/test-runner\": \"^0.13.27\",\n    \"@web/test-runner-commands\": \"^0.6.1\",\n    \"concurrently\": \"^7.1.0\",\n    \"deepmerge\": \"^4.2.2\",\n    \"eslint\": \"^7.32.0\",\n    \"eslint-config-prettier\": \"^7.2.0\",\n    \"husky\": \"^7.0.4\",\n    \"lint-staged\": \"^12.3.7\",\n    \"prettier\": \"^2.6.2\",\n    \"rimraf\": \"^3.0.2\",\n    \"rollup\": \"^2.45.1\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"rollup-plugin-typescript2\": \"^0.31.2\",\n    \"sinon\": \"^13.0.1\",\n    \"tslib\": \"^2.1.0\",\n    \"typescript\": \"^4.1.3\"\n  },\n  \"eslintConfig\": {\n    \"parser\": \"@typescript-eslint/parser\",\n    \"extends\": [\n      \"@open-wc/eslint-config\",\n      \"eslint-config-prettier\"\n    ],\n    \"plugins\": [\n      \"@typescript-eslint\"\n    ],\n    \"rules\": {\n      \"no-unused-vars\": \"off\",\n      \"@typescript-eslint/no-unused-vars\": [\n        \"error\"\n      ],\n      \"import/no-unresolved\": \"off\",\n      \"import/extensions\": [\n        \"error\",\n        \"always\",\n        {\n          \"ignorePackages\": true\n        }\n      ]\n    }\n  },\n  \"prettier\": {\n    \"singleQuote\": true,\n    \"arrowParens\": \"avoid\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"*.ts\": [\n      \"eslint --fix\",\n      \"prettier --write --print-width 140\"\n    ]\n  }\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "\nimport typescript from 'rollup-plugin-typescript2'\nimport resolve from '@rollup/plugin-node-resolve'\nimport {terser} from 'rollup-plugin-terser';\nimport pkg from './package.json';\n\nexport default {\n  input: 'tesla-style-solar-power-card.ts',\n  output: [\n    {\n      file: pkg.main,\n      format: 'cjs',\n    },\n    {\n      file: pkg.module,\n      format: 'es',\n    },\n    {\n      file: 'tesla-style-solar-power-card.js',\n      format: 'iife',\n      name: 'version',\n      plugins: [terser()]\n    }\n  ],\n  plugins: [\n    resolve(),\n    typescript(),\n  ],\n}"
  },
  {
    "path": "src/TeslaStyleSolarPowerCard.ts",
    "content": "/* eslint-disable no-restricted-globals, prefer-template, no-param-reassign, class-methods-use-this, lit-a11y/click-events-have-key-events, no-bitwise, import/extensions */\nimport { LitElement, html, TemplateResult, CSSResult, css } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { HomeAssistant, LovelaceCardConfig /* , LovelaceCardEditor */ } from 'custom-card-helpers';\nimport { TeslaStyleSolarPowerCardConfig } from './models/TeslaStyleSolarPowerCardConfig';\n\n/* import './components/editor'; */\n\nimport { SensorElement } from './models/SensorElement';\nimport { BubbleData } from './models/BubbleData';\nimport { HtmlWriterForPowerCard } from './services/HtmlWriterForPowerCard';\nimport { HtmlResizeForPowerCard } from './services/HtmlResizeForPowerCard';\n// import { localize } from './localize/localize';\n\n// This puts your card into the UI card picker dialog\n(window as any).customCards = (window as any).customCards || [];\n(window as any).customCards.push({\n  type: 'tesla-style-solar-power-card',\n  name: 'Tesla Style Solar Power Card',\n  description: 'A Solar Power Visualization with svg paths that mimmicks the powerwall app of tesla 2',\n});\nexport class TeslaStyleSolarPowerCard extends LitElement {\n  @property({ attribute: false }) public hass!: HomeAssistant;\n\n  @property() private config!: TeslaStyleSolarPowerCardConfig;\n\n  @property({ attribute: false }) public solarCardElements: Map<string, SensorElement> = new Map();\n  \n  @property() private oldWidth = 100;\n\n  public pxRate = 4;\n\n  private teslaCardElement?: HTMLElement;\n\n  private htmlWriter: HtmlWriterForPowerCard = new HtmlWriterForPowerCard(this, this.hass);\n\n  @property({ type: String }) title = 'Hey there';\n\n  @property({ type: Number }) counter = 5;\n\n  __increment() {\n    this.counter += 1;\n  }\n\n  private error: string = '';\n\n  public setConfig(config: LovelaceCardConfig): void {\n    if (!config) {\n      // throw new Error(localize('common.invalid_configuration'));\n    }\n\n    if (config.test_gui) {\n      // getLovelace().setEditMode(true);\n    }\n\n    this.config = {\n      ...config,\n    };\n\n    if (this.config.grid_icon == null) this.config.grid_icon = 'mdi:transmission-tower';\n    if (this.config.generation_icon == null) this.config.generation_icon = 'mdi:solar-panel-large';\n    if (this.config.house_icon == null) this.config.house_icon = 'mdi:home';\n    if (this.config.battery_icon == null) this.config.battery_icon = 'mdi:battery-medium';\n    if (this.config.appliance1_icon == null) this.config.appliance1_icon = 'mdi:car-sports';\n    if (this.config.appliance2_icon == null) this.config.appliance2_icon = 'mdi:air-filter';\n    if (this.config.speed_factor == null) this.config.speed_factor = 0.04;\n\n    this.createSolarCardElements();\n    if (!this.config.energy_flow_diagramm) {\n      const obj = this;\n      setInterval(this.animateCircles, 15, obj);\n    }\n  }\n\n  private createSolarCardElements(): void {\n    Object.keys(this.config).forEach(key => {\n      if (this.config[key] != null && key.indexOf('_entity') > 5) {\n        // only filled entity config elements\n        const sensorName = this.config[key].toString();\n        this.solarCardElements.set(key, new SensorElement(sensorName, key));\n      }\n    });\n  }\n\n  public getCardSize() {\n    return 5;\n  }\n\n  /*\n  public static async getConfigElement(): Promise<LovelaceCardEditor> {\n    return document.createElement('tesla-style-solar-power-card-editor');\n  }\n  */\n\n  public static getStubConfig(): Record<string, any> {\n    return {};\n  }\n\n  /* ** LitElement process functions ** */\n  async firstUpdated(): Promise<void> {\n    // Give the browser a chance to paint\n    await new Promise(r => setTimeout(r, 0));\n    const realWidth = this.getBoundingClientRect().width\n    this.oldWidth = HtmlResizeForPowerCard.changeStylesDependingOnWidth(this, this.solarCardElements, realWidth, this.oldWidth);\n  }\n\n  public connectedCallback(): void {\n    super.connectedCallback();\n    this.redraw = this.redraw.bind(this);\n    window.addEventListener('resize', this.redraw);\n  }\n\n  public shouldUpdate(changedProperties: any): boolean {\n    let obj: any;\n    obj = this;\n    if (!this.config.energy_flow_diagramm) {\n      requestAnimationFrame(timestamp => {\n        obj.updateAllCircles(timestamp);\n      });\n    }\n    obj = this;\n\n    // Update only when our values in hass changed\n    let update = true;\n    Array.from(changedProperties.keys()).some((propName: any) => {\n      const oldValue = changedProperties.get(propName);\n      if (propName === 'hass' && oldValue) {\n        update = update && this.sensorChangeDetected(oldValue);\n      }\n      return !update;\n    });\n    return update;\n  }\n\n  private sensorChangeDetected(oldValue: any): boolean {\n    let change = false;\n    this.solarCardElements.forEach((_solarSensor, key) => {\n      if (\n        this.hass.states[this.config[key]] !== undefined &&\n        this.hass.states[this.config[key]].state !== oldValue.states[this.config[key]].state\n      ) {\n        change = true;\n      }\n    });\n    return change;\n  }\n\n  public async performUpdate(): Promise<void> {\n    this.error = '';\n    this.solarCardElements.forEach(solarSensor => {\n      try {\n        solarSensor.setValueAndUnitOfMeasurement(\n          this.hass.states[solarSensor.entity].state,\n          this.hass.states[solarSensor.entity].attributes.unit_of_measurement\n        );\n        solarSensor.setSpeed(this.config.speed_factor);\n      } catch (err) {\n        this.error += \" Configured '\" + solarSensor.entity + \"' entity was not found. \";\n      }\n    });\n    if (this.config.energy_flow_diagramm) {\n      this.setEnergyFlowDiagramm();\n    }\n    if (this.config.change_house_bubble_color_with_flow) {\n      this.colourHouseBubbleDependingOnHighestInput();\n    }\n    super.performUpdate();\n  }\n\n  /* ****  render functions ****** */\n  protected render(): TemplateResult | void {\n    if (this.error !== '') return this._showError();\n\n    let newWidth = this.getBoundingClientRect().width;\n    if(newWidth < 200) newWidth = 250;\n\n    this.pxRate = newWidth / 100;\n    \n\n    let gap: number;\n    if (this.config.show_gap !== undefined && this.config.show_gap) {\n      gap = 2 * this.pxRate;\n    } else {\n      gap = 0;\n    }\n\n    const half = 22 * this.pxRate;\n    //\n    return html`\n      <ha-card .header=${this.config.name} tabindex=\"0\">\n        <div id=\"tesla-style-solar-power-card\">\n          ${this.writeGenerationIconBubble()}\n          <div class=\"acc_center\">\n            <div class=\"acc_center_container\">\n              ${this.writeGridIconBubble()}\n              <div\n                class=\"acc_line power_lines\"\n                style=\"\n                height:${42 * this.pxRate + 'px'};\n                width:${42 * this.pxRate + 'px'};\n                top:${0 * this.pxRate + 'px'};\n                left:${28 * this.pxRate + 'px'}\"\n              >\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  viewBox=\"${'0 0 ' + 42 * this.pxRate + ' ' + 42 * this.pxRate}\"\n                  preserveAspectRatio=\"xMinYMax slice\"\n                  style=\"height:${42 * this.pxRate + 'px'};width:${42 * this.pxRate + 'px'}\"\n                >\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'generation_to_house_entity',\n                    'M' +\n                      (half - this.pxRate + gap) +\n                      ',0' +\n                      'C' +\n                      (half - this.pxRate + gap) +\n                      ',' +\n                      (half - gap) +\n                      ' ' +\n                      (half - this.pxRate + gap) +\n                      ',' +\n                      (half - gap) +\n                      ' ' +\n                      half * 2 +\n                      ',' +\n                      (half - gap)\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'grid_to_house_entity',\n                    'M0,' +\n                      half +\n                      ' ' +\n                      'C' +\n                      (half - this.pxRate) +\n                      ',' +\n                      half +\n                      ' ' +\n                      (half - this.pxRate) +\n                      ',' +\n                      half +\n                      ' ' +\n                      (half - this.pxRate) * 2 +\n                      ',' +\n                      half\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'generation_to_grid_entity',\n                    'M' +\n                      (half - this.pxRate - gap) +\n                      ',0 ' +\n                      'C' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half - gap) +\n                      ' ' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half - gap) +\n                      ' 0,' +\n                      (half - gap)\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'grid_to_battery_entity',\n                    'M0,' +\n                      (half + gap) +\n                      ' ' +\n                      'C' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      half * 2\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'battery_to_grid_entity',\n                    'M' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      half * 2 +\n                      ' ' +\n                      'C' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      (half - this.pxRate - gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      '0,' +\n                      (half + gap)\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'generation_to_battery_entity',\n                    'M' +\n                      (half - this.pxRate) +\n                      ',0 ' +\n                      'C' +\n                      (half - this.pxRate) +\n                      ',0 ' +\n                      (half - this.pxRate) +\n                      ',' +\n                      half * 2 +\n                      ' ' +\n                      (half - this.pxRate) +\n                      ',' +\n                      half * 2\n                  )}\n                  ${this.htmlWriter.writeCircleAndLine(\n                    'battery_to_house_entity',\n                    'M' +\n                      (half - this.pxRate + gap) +\n                      ',' +\n                      half * 2 +\n                      ' ' +\n                      'C' +\n                      (half - this.pxRate + gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      (half - this.pxRate + gap) +\n                      ',' +\n                      (half + gap) +\n                      ' ' +\n                      half * 2 +\n                      ',' +\n                      (half + gap)\n                  )}\n                </svg>\n              </div>\n\n              ${this.writeHouseIconBubble()} ${this.writeApplianceIconBubble(1)}\n              ${this.htmlWriter.writeAppliancePowerLineAndCircle(1, 'M5,' + 12 * this.pxRate + ' C5,' + 12 * this.pxRate + ' 5,0 5,0')}\n              ${this.writeApplianceIconBubble(2)}\n              ${this.htmlWriter.writeAppliancePowerLineAndCircle(2, 'M5,0 C5,0 5,' + 11 * this.pxRate + ' 5,' + 11 * this.pxRate)}\n            </div>\n          </div>\n          <div class=\"acc_bottom\">${this.writeBatteryIconBubble()}</div>\n        </div>\n      </ha-card>\n    `;\n  }\n\n  private writeGenerationIconBubble(): TemplateResult {\n    const generationEntities = ['generation_to_grid_entity', 'generation_to_house_entity', 'generation_to_battery_entity'];\n    \n    const bubbleData:BubbleData = this.calculateIconBubbleData(\n      generationEntities, \n      'generation_entity', \n      'generation_extra_entity');\n\n    bubbleData.cssSelector = 'acc_top';\n    bubbleData.icon = this.config.generation_icon;\n\n    return this.htmlWriter.writeBatteryBubbleDiv(bubbleData);\n  }\n\n  private writeGridIconBubble(): TemplateResult {\n    const gridEntities = ['-generation_to_grid_entity', 'grid_to_house_entity', '-battery_to_grid_entity', 'grid_to_battery_entity'];\n    \n    const bubbleData:BubbleData = this.calculateIconBubbleData(\n      gridEntities, \n      'grid_entity', \n      'grid_extra_entity');\n\n    bubbleData.cssSelector = 'acc_left';\n    bubbleData.icon = this.config.grid_icon;\n\n    return this.htmlWriter.writeBatteryBubbleDiv(bubbleData);\n  }\n\n  private writeHouseIconBubble(): TemplateResult {\n    \n    let houseEntities:Array<string>;\n    if(this.config.house_without_appliances_values){\n      houseEntities = ['generation_to_house_entity', 'grid_to_house_entity', 'battery_to_house_entity', '-appliance1_consumption_entity','-appliance2_consumption_entity'];\n    } else {\n      houseEntities = ['generation_to_house_entity', 'grid_to_house_entity', 'battery_to_house_entity'];\n    }\n\n\n    const bubbleData:BubbleData = this.calculateIconBubbleData(\n      houseEntities, \n      'house_entity', \n      'house_extra_entity');\n\n    bubbleData.cssSelector = 'acc_right';\n    bubbleData.icon = this.config.house_icon;\n\n\n    return this.htmlWriter.writeBatteryBubbleDiv(bubbleData);\n  }\n\n  private writeBatteryIconBubble(): TemplateResult {\n    const batteryEntities = [\n      'generation_to_battery_entity',\n      'grid_to_battery_entity',\n      '-battery_to_house_entity',\n      '-battery_to_grid_entity',\n    ];\n    const bubbleData:BubbleData = this.calculateIconBubbleData(\n      batteryEntities, \n      'battery_entity', \n      'battery_extra_entity');\n\n    bubbleData.cssSelector = 'acc_bottom';\n    bubbleData.icon = this.config.battery_icon;\n\n    return this.htmlWriter.writeBatteryBubbleDiv(bubbleData);\n  }\n\n  private writeApplianceIconBubble(applianceNumber: number): TemplateResult {\n    const applianceEntities = ['appliance' + applianceNumber + '_consumption_entity'];\n\n\n    const bubbleData:BubbleData = this.calculateIconBubbleData(\n      applianceEntities, \n      'appliance' + applianceNumber + '_consumption_entity', \n      'appliance' + applianceNumber + '_extra_entity');\n\n    bubbleData.cssSelector = 'acc_appliance' + applianceNumber;\n    bubbleData.icon = this.config['appliance' + applianceNumber + '_icon'];\n\n    return this.htmlWriter.writeBatteryBubbleDiv(bubbleData);\n  }\n\n  private calculateIconBubbleData(\n    entitiesForMainValue: Array<string>,\n    bubbleClickEntitySlot: string | null = null,\n    extraEntitySlot: string | null = null,\n  ): BubbleData {\n    \n    let isSubstractionEntity = false;\n    const bubbleData = new BubbleData;\n    bubbleData.clickEntitySlot = bubbleClickEntitySlot;\n\n    entitiesForMainValue.forEach((entityHolder: string) => {\n      if (entityHolder.substring(0, 1) === '-') {\n        entityHolder = entityHolder.substring(1);\n        isSubstractionEntity = true;\n      }\n      const divSolarElement = this.solarCardElements.get(entityHolder);\n\n      if (divSolarElement !== null && divSolarElement?.value !== undefined) {\n        bubbleData.noEntitiesWithValueFound = false;\n        bubbleData.mainValue = isSubstractionEntity ? (bubbleData.mainValue - divSolarElement?.value) : (bubbleData.mainValue + divSolarElement?.value);\n        bubbleData.mainValue = ((bubbleData.mainValue * 100) | 0) / 100;\n        bubbleData.mainUnitOfMeasurement = divSolarElement?.unitOfMeasurement;\n      }\n      isSubstractionEntity = false;\n    });\n\n    if (extraEntitySlot !== null) {\n      const extraEntity = this.solarCardElements.get(extraEntitySlot);\n      bubbleData.extraValue = extraEntity?.value;\n      bubbleData.extraUnitOfMeasurement = extraEntity?.unitOfMeasurement;\n    }\n\n    if (bubbleClickEntitySlot !== null) {\n      bubbleData.clickEntityHassState = this.hass.states[this.config[bubbleClickEntitySlot]];\n    }\n\n    if (this.showKW(bubbleData.mainValue)) {\n      bubbleData.mainValue = this.roundValue(bubbleData.mainValue / 1000);\n      bubbleData.mainUnitOfMeasurement = 'kW';\n    }\n    return bubbleData;\n  }\n\n  private showKW(value: number) {\n    if (this.config.show_w_not_kw) {\n      return false;\n    }\n    if (this.config.threshold_in_k !== undefined && Math.abs(value) < this.config.threshold_in_k * 1000) {\n      return false;\n    }\n\n    return true;\n  }\n\n  private roundValue(value: number): number {\n    let roundedValue: number;\n\n    if (value > 0.1) {\n      roundedValue = (Math.round((value + Number.EPSILON) * 10) | 0) / 10;\n    } else {\n      roundedValue = (Math.round((value + Number.EPSILON) * 100) | 0) / 100;\n    }\n    return roundedValue;\n  }\n\n  private animateCircles(obj: any) {\n    requestAnimationFrame(timestamp => {\n      obj.updateAllCircles(timestamp);\n    });\n  }\n\n  public updateAllCircles(timestamp: number): void {\n    // console.log('updating all circles')\n    this.solarCardElements.forEach((_solarSensor, key) => {\n      const element = this.solarCardElements.get(key);\n      if (element !== undefined) this.updateOneCircle(timestamp, element);\n    });\n  }\n\n  private updateOneCircle(timestamp: number, entity: SensorElement) {\n    if (this.shadowRoot == null) return;\n    const teslaCardElement = <HTMLElement>this.shadowRoot.querySelector('#tesla-style-solar-power-card');\n    if (teslaCardElement == null) return;\n    entity.line = <SVGPathElement>teslaCardElement.querySelector('#' + entity.entitySlot + '_line');\n    if (entity.line === null) return;\n    const lineLength = entity.line.getTotalLength();\n    if (isNaN(lineLength)) return;\n    entity.circle = <SVGPathElement>teslaCardElement.querySelector('#' + entity.entitySlot + '_circle');\n    if (entity.speed === 0) {\n      entity.circle.setAttribute('visibility', 'hidden');\n      if (this.config.hide_inactive_lines) entity.line.setAttribute('visibility', 'hidden');\n      return;\n    }\n\n    entity.circle.setAttribute('visibility', 'visible');\n    if (this.config.hide_inactive_lines) {\n      entity.line.setAttribute('visibility', 'visible');\n    }\n    if (entity.prevTimestamp === 0) {\n      entity.prevTimestamp = timestamp;\n      entity.currentDelta = 0;\n    }\n\n    entity.currentDelta += Math.abs(entity.speed) * (timestamp - entity.prevTimestamp);\n    let percentageDelta = entity.currentDelta / lineLength;\n    if (entity.speed > 0) {\n      if (percentageDelta >= 1 || isNaN(percentageDelta)) {\n        entity.currentDelta = 0;\n        percentageDelta = 0.01;\n      }\n    } else {\n      percentageDelta = 1 - percentageDelta;\n      if (percentageDelta <= 0 || isNaN(percentageDelta)) {\n        entity.currentDelta = 0;\n        percentageDelta = 1;\n      }\n    }\n\n    const point = entity.line.getPointAtLength(lineLength * percentageDelta);\n    entity.circle.setAttributeNS(null, 'cx', point.x.toString());\n    entity.circle.setAttributeNS(null, 'cy', point.y.toString());\n    entity.prevTimestamp = timestamp;\n  }\n\n  private colourHouseBubbleDependingOnHighestInput() {\n    if (this.shadowRoot == null) return;\n    const teslaCardElement = <HTMLElement>this.shadowRoot.querySelector('#tesla-style-solar-power-card');\n    if (teslaCardElement == null) return;\n\n    const houseEntities = ['generation_to_house_entity', 'grid_to_house_entity', 'battery_to_house_entity'];\n    let highestEntity: SensorElement | null = null;\n    let highestEntityHolder = '';\n\n    houseEntities.forEach(entityHolder => {\n      const divSolarElement = this.solarCardElements.get(entityHolder);\n      if (divSolarElement !== null && divSolarElement?.value !== undefined) {\n        if (highestEntity == null || divSolarElement?.value > highestEntity.value) {\n          highestEntityHolder = entityHolder;\n          highestEntity = divSolarElement;\n        }\n      }\n    });\n\n    switch (highestEntityHolder) {\n      case 'generation_to_house_entity':\n        this.colourBubble('.house_entity', teslaCardElement, 'warning');\n        this.colourBubble('.appliance1_consumption_entity', teslaCardElement, 'warning');\n        this.colourBubble('.appliance2_consumption_entity', teslaCardElement, 'warning');\n        this.colourLineAndCircle('#appliance1_consumption_entity', teslaCardElement, 'warning');\n        this.colourLineAndCircle('#appliance2_consumption_entity', teslaCardElement, 'warning');\n        break;\n      case 'battery_to_house_entity':\n        this.colourBubble('.house_entity', teslaCardElement, 'success');\n        this.colourBubble('.appliance1_consumption_entity', teslaCardElement, 'success');\n        this.colourBubble('.appliance2_consumption_entity', teslaCardElement, 'success');\n        this.colourLineAndCircle('#appliance1_consumption_entity', teslaCardElement, 'success');\n        this.colourLineAndCircle('#appliance2_consumption_entity', teslaCardElement, 'success');\n        break;\n      case 'grid_to_house_entity':\n        this.colourBubble('.house_entity', teslaCardElement, 'info');\n        this.colourBubble('.appliance1_consumption_entity', teslaCardElement, 'info');\n        this.colourBubble('.appliance2_consumption_entity', teslaCardElement, 'info');\n        this.colourLineAndCircle('#appliance1_consumption_entity', teslaCardElement, 'info');\n        this.colourLineAndCircle('#appliance2_consumption_entity', teslaCardElement, 'info');\n        break;\n      default:\n    }\n  }\n\n  private colourBubble(elementName: string, teslaCardElement: HTMLElement, colour: string) {\n    const element = <HTMLElement>teslaCardElement.querySelector(elementName);\n\n    if (element === null) return;\n\n    element.style.color = 'var(--' + colour + '-color)';\n    element.style.border = '1px solid var(--' + colour + '-color)';\n  }\n\n  private colourLineAndCircle(elementName: string, teslaCardElement: HTMLElement, colour: string) {\n    const elementLine = <HTMLElement>teslaCardElement.querySelector(elementName + '_line');\n    const elementCircle = <HTMLElement>teslaCardElement.querySelector(elementName + '_circle');\n\n    if (elementLine === null) return;\n    elementLine.style.stroke = 'var(--' + colour + '-color)';\n    elementCircle.style.fill = 'var(--' + colour + '-color)';\n  }\n\n  private setEnergyFlowDiagramm() {\n    if (this.shadowRoot == null) return;\n    const teslaCardElement = <HTMLElement>this.shadowRoot.querySelector('#tesla-style-solar-power-card');\n\n    if (teslaCardElement == null) return;\n\n    this.solarCardElements.forEach((_solarSensor, key) => {\n      const element = this.solarCardElements.get(key);\n\n      let width = 1;\n      if (teslaCardElement == null) return;\n      const entityLine = <SVGPathElement>teslaCardElement.querySelector('#' + key + '_line');\n      if (entityLine != null && element !== undefined) {\n        const entityCircle = <SVGPathElement>teslaCardElement.querySelector('#' + key + '_circle');\n        entityCircle.style.visibility = 'hidden';\n        if (this.config.energy_flow_diagramm_lines_factor === undefined) this.config.energy_flow_diagramm_lines_factor = 2;\n        if (element?.unitOfMeasurement.toUpperCase() === 'W') {\n          width = (Math.floor(element?.value / 100) / 10) * this.config.energy_flow_diagramm_lines_factor;\n        } else {\n          width = (Math.floor(element?.value * 10) / 10) * this.config.energy_flow_diagramm_lines_factor;\n        }\n        if (width <= 0.1 && width !== 0) width = 0.1;\n        entityLine.style.strokeWidth = width + 'px';\n      }\n    });\n  }\n\n  private redraw(ev: UIEvent) {\n    if (this.hass && this.config && ev.type === 'resize') {\n      const realWidth = this.getBoundingClientRect().width\n      this.oldWidth = HtmlResizeForPowerCard.changeStylesDependingOnWidth(this, this.solarCardElements, realWidth, this.oldWidth);\n    }\n  }\n\n  /* ******* actions ******** */\n  private _showWarning(warning: string): TemplateResult {\n    return html` <hui-warning>${warning}</hui-warning> `;\n  }\n\n  private _showError(): TemplateResult {\n    // const errorCard = <LovelaceCard>document.createElement('hui-error-card');\n    // eslint-disable-next-line no-console\n    console.log(this.error); \n    return html`\n      <hui-warning\n        ><div>\n          ERROR:<br />\n          ${this.error}\n        </div></hui-warning\n      >\n    `;\n  }\n\n  /* ******* style ******** */\n\n  static get styles(): CSSResult {\n    return css`\n    #tesla-style-solar-power-card{\n      margin:auto;\n      display:table;\n      padding: 10px;\n      position: relative;\n    }\n    .acc_container {\n        height: 40px;\n        width: 40px;\n        border: 1px solid black;\n        border-radius: 100px;\n        padding: 22px;\n        color: var(--primary-text-color);\n        border-color: var(--primary-text-color);\n        position:relative;\n        cursor:pointer;\n    }\n    .acc_icon {\n        --mdc-icon-size: 40px;\n    }\n    .acc_text,\n    .acc_text_extra {\n        text-align: center;\n        white-space: nowrap;\n    }\n    .acc_text_extra {\n      overflow: hidden;\n      position: absolute;\n    }\n    .acc_td {\n        vertical-align: top;\n    }\n    .acc_center .acc_td{\n      position:relative;\n    }\n    .acc_top .acc_container,\n    .acc_bottom .acc_container{\n      margin:auto;\n    }\n    .acc_center{\n      display:flex;\n    }\n    .acc_center_container{\n      display:inline-block;\n      margin: 0px auto;\n      margin-bottom:-5px;\n    }\n\n    .acc_right ,\n    .acc_left ,\n    .acc_line{\n      display:inline-block;\n      margin-right:-4px\n    }\n    .acc_left {\n      vertical-align: top;\n    }\n    .acc_right {\n      margin-right:0px;\n    }\n    #battery_to_house_entity_line,\n    #generation_to_house_entity_line,\n    #grid_to_house_entity_line,\n    #generation_to_battery_entity_line,\n    #grid_feed_in_entity_line,\n    #generation_to_grid_entity_line,\n    #battery_to_grid_entity_line,\n    #grid_to_battery_entity_line,\n    #appliance1_consumption_entity_line,\n    #appliance2_consumption_entity_line{\n      stroke:var(--info-color);\n      fill:none;\n      stroke-width:1;\n    }\n\n    .generation_entity {\n      border: 1px solid var(--warning-color);\n    }\n    .generation_entity .acc_icon,\n    .generation_entity{\n      color: var(--warning-color);\n    }\n    .house_entity{\n      border: 1px solid var(--info-color);\n    }\n    .appliance1_consumption_entity,\n    .appliance2_consumption_entity {\n      border: 1px solid var(--info-color);\n    }\n    .house_entity,\n    .appliance1_consumption_entity,\n    .appliance2_consumption_entity{\n      color: var(--info-color);\n    }\n    #generation_to_house_entity_line,\n    #generation_to_grid_entity_line,\n    #generation_to_battery_entity_line{\n      stroke:var(--warning-color);\n    }\n    #grid_to_battery_entity_circle,\n    #grid_to_house_entity_circle,\n    #appliance1_consumption_entity_circle,\n    #appliance2_consumption_entity_circle{\n      fill:var(--info-color);\n    }\n    #generation_to_house_entity_circle,\n    #generation_to_grid_entity_circle,\n    #generation_to_battery_entity_circle{\n      fill:var(--warning-color);\n    }\n    #battery_to_house_entity_line,\n    #battery_to_grid_entity_line{\n      stroke:var(--success-color);\n    }\n    #battery_to_house_entity_circle,\n    #battery_to_grid_entity_circle{\n      fill:var(--success-color);\n    }\n    .battery_extra_entity,\n    .battery_entity{\n      border: 1px solid var(--success-color);\n      color: var(--success-color);\n    }\n    .battery_extra_text{\n      position:absolute;\n      top:8px;\n    }\n    br.clear {\n      clear:both;\n    }\n    .power_lines svg{\n      transform: translateZ(0);\n      display:inline-block;\n    }\n    .acc_center .acc_td.acc_appliance1,\n    .acc_center .acc_td.acc_appliance2 {\n      position: absolute;\n      right: 10px;\n    `;\n  }\n}\n"
  },
  {
    "path": "src/components/editor.ts",
    "content": "/* eslint-disable @typescript-eslint/no-explicit-any, no-param-reassign, camelcase, lit/no-useless-template-literals, lit-a11y/click-events-have-key-events */\nimport { LitElement, html, TemplateResult, CSSResult, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { HomeAssistant, LovelaceCardEditor, ActionConfig, LovelaceCardConfig } from 'custom-card-helpers';\nimport '../types.js';\n\nimport { TeslaStyleSolarPowerCardConfig } from '../models/TeslaStyleSolarPowerCardConfig.js';\n\nconst options = {\n  entities: {\n    icon: 'tune',\n    name: 'Entities',\n    secondary: 'Entities for card to make sense, none are required but you should have a few.',\n    show: false,\n  },\n  actions: {\n    icon: 'gesture-tap-hold',\n    name: 'Actions',\n    secondary: 'Perform actions based on tapping/clicking',\n    show: false,\n    options: {\n      tap: {\n        icon: 'gesture-tap',\n        name: 'Tap',\n        secondary: 'Set the action to perform on tap',\n        show: false,\n      },\n      hold: {\n        icon: 'gesture-tap-hold',\n        name: 'Hold',\n        secondary: 'Set the action to perform on hold',\n        show: false,\n      },\n      double_tap: {\n        icon: 'gesture-double-tap',\n        name: 'Double Tap',\n        secondary: 'Set the action to perform on double tap',\n        show: false,\n      },\n    },\n  },\n  appearance: {\n    icon: 'palette',\n    name: 'Appearance',\n    secondary: 'Customize the name, icon, etc',\n    show: false,\n  },\n};\n\n@customElement('tesla-style-solar-power-card-editor')\nexport class TeslaStyleSolarPowerCardEditor extends LitElement implements LovelaceCardEditor {\n  @property({ attribute: false }) public hass?: HomeAssistant;\n\n  @property() private _config?: LovelaceCardConfig;\n\n  @property() private _toggle?: boolean;\n\n  @property() private _helpers?: any;\n\n  private _initialized = false;\n\n  private _entityMap = new Map();\n\n  public setConfig(config: LovelaceCardConfig): void {\n    this._config = config;\n\n    // this._fillLineEntityMap();\n    this.loadCardHelpers();\n  }\n\n  protected shouldUpdate(): boolean {\n    if (!this._initialized) {\n      this._initialize();\n    }\n\n    return true;\n  }\n\n  get name(): string {\n    return this._config?.name || '';\n  }\n\n  // entities\n  get home_entity(): string {\n    return this._config?.home_entity || '';\n  }\n\n  get battery_entity(): string {\n    return this._config?.battery_consumption_entity || '';\n  }\n\n  get grid_entity(): string {\n    return this._config?.grid_entity || '';\n  }\n\n  get generation_entity(): string {\n    return this._config?.generation_entity || '';\n  }\n\n  get home_extra_entity(): string {\n    return this._config?.home_entity || '';\n  }\n\n  get battery_extra_entity(): string {\n    return this._config?.battery_consumption_entity || '';\n  }\n\n  get grid_extra_entity(): string {\n    return this._config?.grid_entity || '';\n  }\n\n  get generation_extra_entity(): string {\n    return this._config?.generation_entity || '';\n  }\n\n  get grid_to_house_entity(): string {\n    return this._config?.grid_to_house_entity || '';\n  }\n\n  get grid_to_battery_entity(): string {\n    return this._config?.grid_to_battery_entity || '';\n  }\n\n  get battery_to_grid_in_entity(): string {\n    return this._config?.battery_to_grid_entity || '';\n  }\n\n  get generation_to_grid_entity(): string {\n    return this._config?.grid_to_battery_entity || '';\n  }\n\n  get generation_to_house_entity(): string {\n    return this._config?.generation_entity || '';\n  }\n\n  get generation_to_battery_entity(): string {\n    return this._config?.generation_yield_entity || '';\n  }\n\n  get appliance1_consumption_entity(): string {\n    return this._config?.appliance1_consumption_entity || '';\n  }\n\n  get appliance1_extra_entity(): string {\n    return this._config?.appliance1_state_entity || '';\n  }\n\n  get appliance2_consumption_entity(): string {\n    return this._config?.appliance2_consumption_entity || '';\n  }\n\n  get appliance2_extra_entity(): string {\n    return this._config?.appliance2_state_entity || '';\n  }\n\n  get show_w_not_kw(): boolean {\n    return this._config?.show_w_not_kw || false;\n  }\n\n  get show_warning(): boolean {\n    return this._config?.show_warning || false;\n  }\n\n  get show_error(): boolean {\n    return this._config?.show_error || false;\n  }\n\n  get hide_inactive_lines(): boolean {\n    return this._config?.hide_inactive_lines || false;\n  }\n\n  get tap_action(): ActionConfig {\n    return this._config?.tap_action || { action: 'more-info' };\n  }\n\n  get hold_action(): ActionConfig {\n    return this._config?.hold_action || { action: 'none' };\n  }\n\n  get double_tap_action(): ActionConfig {\n    return this._config?.double_tap_action || { action: 'none' };\n  }\n\n  protected render(): TemplateResult | void {\n    if (!this.hass || !this._helpers) {\n      return html``;\n    }\n\n    // The climate more-info has ha-switch and paper-dropdown-menu elements that are lazy loaded unless explicitly done here\n    this._helpers.importMoreInfoControl('climate');\n\n    return html`\n      <div class=\"card-config\">\n        <paper-input\n          .label=\"${this.hass.localize('ui.panel.lovelace.editor.card.generic.title')} (${this.hass.localize(\n            'ui.panel.lovelace.editor.card.config.optional'\n          )})\"\n          .value=${this.name}\n          .configValue=${'name'}\n          @value-changed=${this._valueChanged}\n        ></paper-input>\n        <div class=\"option\" @click=${this._toggleOption} .option=${'entities'}>\n          <div class=\"row\">\n            <ha-icon .icon=${`mdi:${options.entities.icon}`}></ha-icon>\n            <div class=\"title\">${options.entities.name}</div>\n          </div>\n          <div class=\"secondary\">${options.entities.secondary}</div>\n        </div>\n        ${options.entities.show\n          ? html`<div class=\"values\">\n              ${Array.from(this._entityMap).map(entityArr => {\n                const entityName: keyof TeslaStyleSolarPowerCardConfig = entityArr[0];\n                const entityFunction = this[`_${entityName}`];\n                return html`\n                  <ha-entity-picker\n                    label=\"${entityName}\"\n                    @value-changed=${this._valueChanged}\n                    .hass=\"${this.hass}\"\n                    .value=\"${entityFunction}\"\n                    .configValue=${entityName}\n                    @change=\"${this._valueChanged}\"\n                    allow-custom-entity\n                  >\n                  </ha-entity-picker>\n                `;\n              })}\n            </div>`\n          : ''}\n        <div class=\"option\" @click=${this._toggleOption} .option=${'actions'}>\n          <div class=\"row\">\n            <ha-icon .icon=${`mdi:${options.actions.icon}`}></ha-icon>\n            <div class=\"title\">${options.actions.name}</div>\n          </div>\n          <div class=\"secondary\">${options.actions.secondary}</div>\n        </div>\n        ${options.actions.show\n          ? html`\n              <div class=\"values\">\n                <div class=\"option\" @click=${this._toggleAction} .option=${'tap'}>\n                  <div class=\"row\">\n                    <ha-icon .icon=${`mdi:${options.actions.options.tap.icon}`}></ha-icon>\n                    <div class=\"title\">${options.actions.options.tap.name}</div>\n                  </div>\n                  <div class=\"secondary\">${options.actions.options.tap.secondary}</div>\n                </div>\n                ${options.actions.options.tap.show\n                  ? html`\n                      <div class=\"values\">\n                        <paper-item>Action Editors Coming Soon</paper-item>\n                      </div>\n                    `\n                  : ''}\n                <div class=\"option\" @click=${this._toggleAction} .option=${'hold'}>\n                  <div class=\"row\">\n                    <ha-icon .icon=${`mdi:${options.actions.options.hold.icon}`}></ha-icon>\n                    <div class=\"title\">${options.actions.options.hold.name}</div>\n                  </div>\n                  <div class=\"secondary\">${options.actions.options.hold.secondary}</div>\n                </div>\n                ${options.actions.options.hold.show\n                  ? html`\n                      <div class=\"values\">\n                        <paper-item>Action Editors Coming Soon</paper-item>\n                      </div>\n                    `\n                  : ''}\n                <div class=\"option\" @click=${this._toggleAction} .option=${'double_tap'}>\n                  <div class=\"row\">\n                    <ha-icon .icon=${`mdi:${options.actions.options.double_tap.icon}`}></ha-icon>\n                    <div class=\"title\">${options.actions.options.double_tap.name}</div>\n                  </div>\n                  <div class=\"secondary\">${options.actions.options.double_tap.secondary}</div>\n                </div>\n                ${options.actions.options.double_tap.show\n                  ? html`\n                      <div class=\"values\">\n                        <paper-item>Action Editors Coming Soon</paper-item>\n                      </div>\n                    `\n                  : ''}\n              </div>\n            `\n          : ''}\n        <div class=\"option\" @click=${this._toggleOption} .option=${'appearance'}>\n          <div class=\"row\">\n            <ha-icon .icon=${`mdi:${options.appearance.icon}`}></ha-icon>\n            <div class=\"title\">${options.appearance.name}</div>\n          </div>\n          <div class=\"secondary\">${options.appearance.secondary}</div>\n        </div>\n        ${options.appearance.show\n          ? html`\n              <div class=\"values\">\n                <br />\n                <ha-formfield .label=${`Toggle warning ${this.show_warning ? 'off' : 'on'}`}>\n                  <ha-switch\n                    .checked=${this.show_warning !== false}\n                    .configValue=${'show_warning'}\n                    @change=${this._valueChanged}\n                  ></ha-switch>\n                </ha-formfield>\n                <ha-formfield .label=${`Toggle error ${this.show_error ? 'off' : 'on'}`}>\n                  <ha-switch .checked=${this.show_error !== false} .configValue=${'show_error'} @change=${this._valueChanged}></ha-switch>\n                </ha-formfield>\n                <ha-formfield .label=${`Toggle W instead of kW ${this.show_w_not_kw ? 'off' : 'on'}`}>\n                  <ha-switch\n                    .checked=${this.show_w_not_kw !== false}\n                    .configValue=${'show_w_not_kw'}\n                    @change=${this._valueChanged}\n                  ></ha-switch>\n                </ha-formfield>\n                <ha-formfield .label=${`Toggle hiding inactive power lines ${this.hide_inactive_lines ? 'off' : 'on'}`}>\n                  <ha-switch\n                    .checked=${this.hide_inactive_lines !== false}\n                    .configValue=${'hide_inactive_lines'}\n                    @change=${this._valueChanged}\n                  ></ha-switch>\n                </ha-formfield>\n              </div>\n            `\n          : ''}\n      </div>\n    `;\n  }\n\n  private _initialize(): void {\n    if (this.hass === undefined) return;\n    if (this._config === undefined) return;\n    if (this._helpers === undefined) return;\n    this._initialized = true;\n  }\n\n  private async loadCardHelpers(): Promise<void> {\n    this._helpers = await (window as any).loadCardHelpers();\n  }\n\n  private _toggleAction(ev: any): void {\n    this._toggleThing(ev, options.actions.options);\n  }\n\n  private _toggleOption(ev: any): void {\n    this._toggleThing(ev, options);\n  }\n\n  private _toggleThing(ev: any, optionList: any): void {\n    const show = !optionList[ev.target.option].show;\n    for (const [key] of Object.entries(optionList)) {\n      optionList[key].show = false;\n    }\n    optionList[ev.target.option].show = show;\n    this._toggle = !this._toggle;\n  }\n\n  private _valueChanged(ev: any): void {\n    if (!this._config || !this.hass) {\n      return;\n    }\n    const { target } = ev;\n    if (this[`_${target.configValue}`] === target.value) {\n      return;\n    }\n    if (target.configValue) {\n      if (target.value === '') {\n        delete this._config[target.configValue];\n      } else {\n        this._config = {\n          ...this._config,\n          [target.configValue]: target.checked !== undefined ? target.checked : target.value,\n        };\n      }\n    }\n    // fireEvent(this, 'config-changed', { config: this._config }); // this is breaking the card built when terser is activated\n  }\n\n  private _fillLineEntityMap() {\n    if (this._config !== undefined) {\n      this._entityMap.set('home_entity', this._config.home_entity);\n      this._entityMap.set('grid_entity', this._config.grid_entity);\n      this._entityMap.set('generation_entity', this._config.generation_entity);\n      this._entityMap.set('battery_entity', this._config.battery_entity);\n\n      this._entityMap.set('grid_to_house_entity', this._config.grid_to_battery_entity);\n      this._entityMap.set('grid_to_battery_entity', this._config.grid_to_battery_entity);\n      this._entityMap.set('generation_to_grid_entity', this._config.grid_feed_in_entity);\n      this._entityMap.set('generation_to_house_entity', this._config.generation_consumption_entity);\n      this._entityMap.set('generation_to_battery_entity', this._config.generation_to_battery_entity);\n      this._entityMap.set('battery_to_grid_entity', this._config.generation_consumption_entity);\n      this._entityMap.set('battery_to_house_entity', this._config.generation_to_battery_entity);\n      this._entityMap.set('battery_extra_entity', this._config.battery_extra_entity);\n      this._entityMap.set('appliance1_consumption_entity', this._config.appliance1_entity);\n      this._entityMap.set('appliance1_state_entity', this._config.appliance1_state_entity);\n      this._entityMap.set('appliance2_consumption_entity', this._config.appliance2_entity);\n      this._entityMap.set('appliance2_state_entity', this._config.appliance2_state_entity);\n    }\n  }\n\n  private _fillIconEntityMap() {\n    if (this._config !== undefined) {\n      this._entityMap.set('grid_entity', this._config.grid_entity);\n      this._entityMap.set('home_entity', this._config.home_entity);\n      this._entityMap.set('generation_entity', this._config.generation_entity);\n      this._entityMap.set('battery_entity', this._config.battery_entity);\n      this._entityMap.set('appliance1_entity', this._config.appliance1_entity);\n      this._entityMap.set('appliance2_entity', this._config.appliance2_entity);\n    }\n  }\n\n  static get styles(): CSSResult {\n    return css`\n      .option {\n        padding: 4px 0px;\n        cursor: pointer;\n      }\n      .row {\n        display: flex;\n        margin-bottom: -14px;\n        pointer-events: none;\n      }\n      .title {\n        padding-left: 16px;\n        margin-top: -6px;\n        pointer-events: none;\n      }\n      .secondary {\n        padding-left: 40px;\n        color: var(--secondary-text-color);\n        pointer-events: none;\n      }\n      .values {\n        padding-left: 16px;\n        background: var(--secondary-background-color);\n        display: grid;\n      }\n      ha-formfield {\n        padding-bottom: 8px;\n      }\n    `;\n  }\n}\n"
  },
  {
    "path": "src/localize/languages/de.json",
    "content": "\n{\n    \"common\": {\n      \"version\": \"Version\",\n      \"invalid_configuration\": \"Invalid configuration\",\n      \"show_warning\": \"Show Warning\",\n      \"show_error\": \"Show Error\"\n    }\n  }"
  },
  {
    "path": "src/localize/languages/en.json",
    "content": ""
  },
  {
    "path": "src/localize/localize.ts",
    "content": "import * as en from './languages/en.json';\nimport * as nb from './languages/de.json';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst languages: any = {\n  en,\n  nb,\n};\n\nexport function localize(string: string, search = '', replace = ''): string {\n  const lang = (localStorage.getItem('selectedLanguage') || 'en')\n    .replace(/['\"]+/g, '')\n    .replace('-', '_');\n\n  let translated: string;\n\n  try {\n    translated = string.split('.').reduce((o, i) => o[i], languages[lang]);\n  } catch (e) {\n    translated = string.split('.').reduce((o, i) => o[i], languages.en);\n  }\n\n  if (translated === undefined)\n    translated = string.split('.').reduce((o, i) => o[i], languages.en);\n\n  if (search !== '' && replace !== '') {\n    translated = translated.replace(search, replace);\n  }\n  return translated;\n}\n"
  },
  {
    "path": "src/models/BubbleData.ts",
    "content": "import { HassEntity } from 'home-assistant-js-websocket';\n\nexport class BubbleData {\n\n    public mainValue: number = 0;\n    \n    public mainUnitOfMeasurement: string | undefined;\n    \n    public clickEntitySlot: string | null = null;\n    \n    public clickEntityHassState:HassEntity | null = null;\n\n    public icon:string | undefined;\n\n    public cssSelector: string | undefined;\n\n    public extraValue: string | undefined;\n    \n    public extraUnitOfMeasurement: string | undefined;\n\n    public noEntitiesWithValueFound = true;\n\n}"
  },
  {
    "path": "src/models/SensorElement.ts",
    "content": "/* eslint-disable class-methods-use-this, no-bitwise  */\nexport class SensorElement {\n  public speed = 0;\n\n  public startPosition = 0;\n\n  public currentPosition = 0;\n\n  public currentDelta = 0;\n\n  public maxPosition = 30;\n\n  public value: any;\n\n  public unitOfMeasurement = '';\n\n  public accText = '';\n\n  public accTextclassName = 'accText';\n\n  public entity = '';\n\n  public circle?: SVGPathElement;\n\n  public line?: SVGPathElement;\n\n  public color = 'stroke:var(--info-color)';\n\n  public circleColor = 'var(--primary-color)';\n\n  public prevTimestamp = 0;\n\n  public accTextElement = null;\n\n  public entitySlot: string;\n\n  private static readonly SPEEDFACTOR = 0.04;\n\n  constructor(entity: string, enitySlot: string) {\n    this.entity = entity;\n    this.entitySlot = enitySlot;\n    this.value = 0;\n  }\n\n  public setValueAndUnitOfMeasurement(entityState: string | undefined, unitOfMeasurement: string | undefined): void {\n    if (entityState === undefined) {\n      this.value = 0;\n      return;\n    }\n    if (unitOfMeasurement === undefined) {\n      this.value = entityState;\n      return;\n    }\n\n    const valueFromState = parseFloat(entityState);\n\n    switch (unitOfMeasurement) {\n      case 'W':\n      case 'w':\n      case 'kW':\n        this.value = valueFromState;\n        if (unitOfMeasurement === 'kW') {\n          this.value *= 1000;\n        }\n        this.unitOfMeasurement = 'W';\n        this.value = Math.round(this.value);\n        break;\n      case '%':\n        this.value = valueFromState;\n        this.unitOfMeasurement = unitOfMeasurement;\n        break;\n      default:\n        this.value = entityState;\n        this.unitOfMeasurement = unitOfMeasurement;\n    }\n  }\n\n  public setSpeed(factor: number | undefined): void {\n    this.speed = 0;\n    if (Math.abs(this.value) === 0) return;\n\n    let speedFactor: number;\n    if (factor === undefined || factor > 1 || factor <= 0) {\n      speedFactor = SensorElement.SPEEDFACTOR;\n    } else {\n      speedFactor = factor;\n    }\n\n    this.speed = (speedFactor * this.value) / 1000;\n  }\n}\n"
  },
  {
    "path": "src/models/TeslaStyleSolarPowerCardConfig.ts",
    "content": "/* eslint-disable camelcase */\nimport { ActionConfig, LovelaceCardConfig } from 'custom-card-helpers';\n\nexport interface TeslaStyleSolarPowerCardConfig extends LovelaceCardConfig {\n  type: string;\n  name?: string;\n  show_header_toggle?: boolean;\n\n  show_warning?: boolean;\n  show_error?: boolean;\n  show_gap?: boolean;\n  test_gui?: boolean;\n  show_w_not_kw?: any;\n  hide_inactive_lines?: boolean;\n  threshold_in_k?: number;\n  speed_factor?: number;\n  energy_flow_diagramm?: boolean;\n  energy_flow_diagramm_lines_factor?: number;\n  change_house_bubble_color_with_flow?: boolean;\n  house_without_appliances_values?: boolean;\n\n  grid_icon?: string;\n  generation_icon?: string;\n  house_icon?: string;\n  battery_icon?: string;\n  appliance1_icon?: string;\n  appliance2_icon?: string;\n\n  icon_entities?: Map<string, string>;\n  line_entities?: Map<string, string>;\n\n  house_entity?: string;\n  battery_entity?: string;\n  generation_entity?: string;\n  grid_entity?: string;\n\n  grid_to_house_entity?: string;\n  grid_to_battery_entity?: string;\n\n  generation_to_grid_entity?: string;\n  generation_to_battery_entity?: string;\n  generation_to_house_entity?: string;\n\n  battery_to_house_entity?: string;\n  battery_to_grid_entity?: string;\n\n  grid_extra_entity?: string;\n  generation_extra_entity?: string;\n  house_extra_entity?: string;\n  battery_extra_entity?: string;\n\n  appliance1_consumption_entity?: string;\n  appliance1_extra_entity?: string;\n  appliance2_consumption_entity?: string;\n  appliance2_extra_entity?: string;\n\n  tap_action?: ActionConfig;\n  hold_action?: ActionConfig;\n  double_tap_action?: ActionConfig;\n}\n"
  },
  {
    "path": "src/services/HtmlResizeForPowerCard.ts",
    "content": "/* eslint-disable func-names, prefer-template, import/extensions, no-param-reassign, class-methods-use-this, lit-a11y/click-events-have-key-events */\nimport { SensorElement } from '../models/SensorElement';\nimport { TeslaStyleSolarPowerCard } from '../TeslaStyleSolarPowerCard';\n\nexport class HtmlResizeForPowerCard {\n  public static changeStylesDependingOnWidth(\n    teslaCard: TeslaStyleSolarPowerCard,\n    solarCardElements: Map<string, SensorElement>,\n    newWidth: number,\n    oldWidth: number\n  ): number {\n    if (document.readyState !== 'complete' || oldWidth === newWidth)\n      return oldWidth;\n    if (teslaCard.shadowRoot == null) return oldWidth;\n    const teslaCardElement = <HTMLElement>(\n      teslaCard.shadowRoot.querySelector('#tesla-style-solar-power-card')\n    );\n\n    if (teslaCardElement == null) return oldWidth;\n\n    if(newWidth < 200) newWidth = 250;\n\n    const pxRate = newWidth / 100;\n\n    const changeSelectorStyle = function (\n      selector: string,\n      styleAttribute: any,\n      attributeValue: string\n    ) {\n      const selectorElement = <HTMLElement>(\n        teslaCardElement.querySelector(selector)\n      );\n      if (selectorElement !== null)\n        selectorElement.style[styleAttribute] = attributeValue;\n    };\n\n    changeSelectorStyle('.acc_left', 'top', 12 * pxRate + 'px');\n    changeSelectorStyle('.acc_right', 'top', 12 * pxRate + 'px');\n\n    // icons\n    teslaCardElement\n      .querySelectorAll('.acc_container')\n      .forEach((_currentValue, currentIndex, iconContainerItem) => {\n        const iconContainer = <HTMLElement>iconContainerItem[currentIndex];\n        iconContainer.style.height = 9 * pxRate + 'px';\n        iconContainer.style.width = 9 * pxRate + 'px';\n        iconContainer.style.padding = 5 * pxRate + 'px';\n      });\n    teslaCardElement\n      .querySelectorAll('ha-icon')\n      .forEach((_currentValue, currentIndex, icons) => {\n        const icon = <HTMLElement>(\n          icons[currentIndex].shadowRoot?.querySelector('ha-svg-icon')\n        );\n        if (icon != null) {\n          icon.style.height = 9 * pxRate + 'px';\n          icon.style.width = 9 * pxRate + 'px';\n        }\n      });\n    teslaCardElement\n      .querySelectorAll<HTMLElement>('.acc_text')\n      .forEach(icontext => {\n        icontext.style['font-size'] = 3 * pxRate + 'px';\n        icontext.style['margin-top'] = -0.5 * pxRate + 'px';\n        icontext.style.width = 10 * pxRate + 'px';\n      });\n    teslaCardElement\n      .querySelectorAll<HTMLElement>('.acc_text_extra')\n      .forEach(icontextExtra => {\n        icontextExtra.style['font-size'] = 3 * pxRate + 'px';\n        icontextExtra.style.top = 1 * pxRate + 'px';\n        icontextExtra.style.width = 10 * pxRate + 'px';\n      });\n\n    // power lines\n    changeSelectorStyle('.power_lines', 'height', 42 * pxRate + 'px');\n    changeSelectorStyle('.power_lines', 'width', 42 * pxRate + 'px');\n    changeSelectorStyle('.power_lines', 'top', 0 * pxRate + 'px');\n    changeSelectorStyle('.power_lines', 'left', 28 * pxRate + 'px');\n    changeSelectorStyle('.power_lines svg', 'width', 42 * pxRate + 'px');\n    changeSelectorStyle('.power_lines svg', 'height', 42 * pxRate + 'px');\n    changeSelectorStyle(\n      '.power_lines svg',\n      'viewBox',\n      '0 0 ' + 42 * pxRate + ' ' + 42 * pxRate\n    );\n    let selectorElement = <HTMLElement>(\n      teslaCardElement.querySelector('.power_lines svg')\n    );\n    if (selectorElement !== null)\n      selectorElement.setAttribute(\n        'viewBox',\n        '0 0 ' + 42 * pxRate + ' ' + 42 * pxRate\n      );\n    const half = 22 * pxRate;\n\n    changeSelectorStyle(\n      '#generation_to_house_entity_line',\n      'd',\n      'M' +\n        (half-pxRate) +\n        ',0 C' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' ' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' ' +\n        half * 2 +\n        ',' +\n        half\n    );\n    changeSelectorStyle(\n      '#grid_feed_in_entity_line',\n      'd',\n      'M' +\n        (half-pxRate) +\n        ',0 C' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' ' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' 0,' +\n        half\n    );\n    changeSelectorStyle(\n      '#grid_to_house_entity_line',\n      'd',\n      'M0,' +\n        half +\n        ' C' +\n        half +\n        ',' +\n        half +\n        ' ' +\n        half +\n        ',' +\n        half +\n        ' ' +\n        half * 2 +\n        ',' +\n        half\n    );\n    changeSelectorStyle(\n      '#grid_to_battery_entity_line',\n      'd',\n      'M0,' +\n        half +\n        ' C' +\n        half +\n        ',' +\n        half +\n        ' ' +\n        half +\n        ',' +\n        half +\n        ' ' +\n        half +\n        ',' +\n        half * 2\n    );\n    changeSelectorStyle(\n      '#battery_to_house_entity_line',\n      'd',\n      'M' +\n        (half-pxRate) +\n        ',' +\n        half * 2 +\n        ' C' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' ' +\n        (half-pxRate) +\n        ',' +\n        half +\n        ' ' +\n        half * 2 +\n        ',' +\n        half\n    );\n    changeSelectorStyle(\n      '#generation_to_battery_entity_line',\n      'd',\n      'M' +\n        (half-pxRate) +\n        ',0 C' +\n        (half-pxRate) +\n        ',0 ' +\n        (half-pxRate) +\n        ',' +\n        half * 2 +\n        ' ' +\n        (half-pxRate) +\n        ',' +\n        half * 2\n    );\n\n    // appliances\n    [1, 2].forEach(value => {\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line svg',\n        'viewBox',\n        '0 0 ' + ((12*pxRate)-((value-1)*5)) + ' ' + ((12*pxRate)-((value-1)*5))\n      );\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line',\n        'right',\n        (9.5 * pxRate) + 10 + 'px'\n      );\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line',\n        'width',\n        '10px'\n      );\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line',\n        'height',\n        (12 * pxRate ) - (( value - 1) * 5) + 'px'\n      );\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line svg',\n        'width',\n        '10px'\n      );\n      changeSelectorStyle(\n        '.acc_appliance' + value + '_line svg',\n        'height',\n        (12 * pxRate ) - (( value - 1) * 5) + 'px'\n      );\n      selectorElement = <HTMLElement>(\n        teslaCardElement.querySelector('.acc_appliance' + value + '_line_svg')\n      );\n      if (selectorElement !== null)\n        selectorElement.setAttribute(\n          'viewBox',\n          '0 0 ' + ((12*pxRate)-((value-1)*5)) + ' ' + ((12*pxRate)-((value-1)*5))\n        );\n\n      const topElement = <HTMLElement>(\n        teslaCardElement.querySelector('.generation_entity')\n      );\n      if (topElement === null && value === 1 && selectorElement !== null) {\n        changeSelectorStyle(\n          '.acc_center_container',\n          'margin-top',\n          19 * pxRate + 'px'\n        );\n      }\n      const bottomElement = <HTMLElement>(\n        teslaCardElement.querySelector('.battery_entity')\n      );\n      if (bottomElement === null && value === 2 && selectorElement !== null) {\n        changeSelectorStyle(\n          '.acc_center_container',\n          'margin-bottom',\n          19 * pxRate + 'px'\n        );\n      }\n    });\n    const gridElement = <HTMLElement>(\n      teslaCardElement.querySelector('.grid_entity')\n    );\n    if (gridElement === null) {\n      changeSelectorStyle('.generation_entity', 'margin', '0px');\n      changeSelectorStyle('.battery_entity', 'margin', '0px');\n      changeSelectorStyle('.power_lines', 'width', 30 * pxRate + 'px');\n      selectorElement = <HTMLElement>(\n        teslaCardElement.querySelector('.power_lines svg')\n      );\n      if (selectorElement !== null)\n        selectorElement.setAttribute(\n          'viewBox',\n          12 * pxRate + ' 0 ' + 42 * pxRate + ' ' + 42 * pxRate\n        );\n    }\n\n    changeSelectorStyle('.acc_appliance1', 'top', 10 + 'px');\n    changeSelectorStyle('.acc_appliance1_line', 'top', 19 * pxRate + 12 + 'px');\n    changeSelectorStyle('.acc_appliance2', 'bottom', 10 + 'px');\n    changeSelectorStyle('.acc_appliance2_line', 'bottom', 19 * pxRate + 12 +'px');\n\n    return newWidth;\n  }\n}\n"
  },
  {
    "path": "src/services/htmlWriterForPowerCard.ts",
    "content": "/* eslint-disable no-param-reassign,  import/extensions, prefer-template, class-methods-use-this, lit-a11y/click-events-have-key-events, lines-between-class-members */\nimport { html, TemplateResult } from 'lit';\nimport { HomeAssistant } from 'custom-card-helpers';\nimport { HassEntity } from 'home-assistant-js-websocket';\nimport { SensorElement } from '../models/SensorElement';\nimport { TeslaStyleSolarPowerCard } from '../TeslaStyleSolarPowerCard';\nimport { BubbleData } from '../models/BubbleData';\n\nexport class HtmlWriterForPowerCard {\n  private teslaCard: TeslaStyleSolarPowerCard;\n\n  private solarCardElements: Map<string, SensorElement>;\n\n  private pxRate: number;\n\n  private hass: HomeAssistant;\n\n  public constructor(teslaCard: TeslaStyleSolarPowerCard, hass: HomeAssistant) {\n    this.teslaCard = teslaCard;\n    this.solarCardElements = teslaCard.solarCardElements;\n    this.pxRate = teslaCard.pxRate;\n    this.hass = hass;\n  }\n\n  public writeBubbleDiv(bubbleData: BubbleData\n  ): TemplateResult {\n    \n    if(bubbleData.noEntitiesWithValueFound) return html``;\n\n    return html` <div class=\"acc_td ${bubbleData.cssSelector}\">\n      <div\n        class=\"acc_container ${bubbleData.clickEntitySlot}\"\n        style=\"${'width:' + 9 * this.pxRate + 'px; height: ' + 9 * this.pxRate + 'px; padding:' + 5 * this.pxRate + 'px;'}\"\n        @click=\"${() => this._handleClick(bubbleData.clickEntityHassState)}\"\n      >\n        ${bubbleData.extraValue !== null\n          ? html` <div\n              class=\"acc_text_extra\"\n              style=\"font-size:${3 * this.pxRate + 'px'};\n                        top: ${1 * this.pxRate + 'px'};\n                        width: ${10 * this.pxRate + 'px'};\"\n            >${bubbleData.extraValue} ${bubbleData.extraUnitOfMeasurement}\n            </div>`\n          : html``}\n        <ha-icon class=\"acc_icon\" icon=\"${bubbleData.icon}\"></ha-icon>\n        <div class=\"acc_text\" style=\"font-size:${3 * this.pxRate + 'px'}; margin-top:${-0.5 * this.pxRate + 'px'}; width: ${10 * this.pxRate + 'px'}\">\n          ${bubbleData.mainValue} ${bubbleData.mainUnitOfMeasurement}\n        </div>\n      </div>\n    </div>`;\n  }\n\n  public writeBatteryBubbleDiv(bubbleData:BubbleData): TemplateResult {\n    if (bubbleData.extraValue !== undefined) {\n      if (bubbleData.icon === 'mdi:battery-medium' || bubbleData.icon === 'mdi:battery'){\n        bubbleData.icon = this.getBatteryIcon(parseFloat(bubbleData.extraValue), bubbleData.mainValue);\n      }\n    }\n    return this.writeBubbleDiv(bubbleData);\n  }\n\n  private getBatteryIcon(batteryValue: number, batteryChargeDischargeValue: number) {\n    let TempSocValue = batteryValue;\n    if (batteryValue <= 5) TempSocValue = 0;\n\n    const batteryStateRoundedValue = Math.ceil(TempSocValue / 10) * 10;\n    let batteryStateIconString = '-' + batteryStateRoundedValue.toString();\n\n    // show charging icon beside battery state\n    let batteryCharging: string = '-charging';\n    if (batteryChargeDischargeValue <= 0) {\n      batteryCharging = '';\n    }\n\n    if (batteryStateRoundedValue === 100) batteryStateIconString = ''; // full\n    if (batteryStateRoundedValue <= 5) batteryStateIconString = '-outline'; // empty\n    return 'mdi:battery' + batteryCharging + batteryStateIconString;\n  }\n\n  public writeAppliancePowerLineAndCircle(applianceNumber: number, pathDAttribute: string) {\n    const divEntity = this.solarCardElements.get('appliance' + applianceNumber + '_consumption_entity');\n    if (divEntity == null) return html``;\n    const height = 12;\n    let verticalPosition: string;\n    if (applianceNumber === 1) {\n      verticalPosition = 'top:' + 22.5 * this.pxRate + 'px;';\n    } else {\n      verticalPosition = 'bottom:' + 15 * this.pxRate + 'px;';\n    }\n    return html` <div\n      class=\"acc_line acc_appliance${applianceNumber}_line\"\n      style=\"\n        height:${(height * this.pxRate)-((applianceNumber-1)*5)+'px'}\n        width:10px};\n        right:${(9.5 * this.pxRate) + 10 + 'px'};\n        ${verticalPosition}\n        position:absolute\"\n    >\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox='${'0 0 '+ ((12*this.pxRate)-((applianceNumber-1)*5)) + ' ' +((12*this.pxRate)-((applianceNumber-1)*5))}'\n        preserveAspectRatio=\"xMinYMax slice\"\n        style=\"height:${(height * this.pxRate)-((applianceNumber-1)*5)+'px'};width:10px}\"\n        class=\"acc_appliance${applianceNumber}_line_svg\"\n      >\n        ${this.writeCircleAndLine('appliance' + applianceNumber + '_consumption_entity', pathDAttribute)}\n      </svg>\n    </div>`;\n  }\n\n  public writeCircleAndLine(sensorName: string, pathDAttribute: string) {\n    const entity = this.solarCardElements.get(sensorName);\n    if (entity == null) return html``;\n    return html`<svg>\n      <circle r=\"4\" cx=\"${entity.startPosition.toString()}\" cy=\"4\" fill=\"${entity.color}\" id=\"${sensorName + '_circle'}\"></circle>\n      <path d=\"${pathDAttribute}\" id=\"${sensorName + '_line'}\"></path>\n    </svg>`;\n  }\n\n  private _handleClick(stateObj: HassEntity | null) {\n    if (stateObj == null) return;\n    const event = <any>new Event('hass-more-info', {\n      bubbles: true,\n      cancelable: true,\n      composed: true,\n    });\n    event.detail = { entityId: stateObj.entity_id };\n    if (this.teslaCard.shadowRoot == null) return;\n    this.teslaCard.shadowRoot.dispatchEvent(event);\n  }\n}\n"
  },
  {
    "path": "src/translations/languages/de.json",
    "content": "\n{\n    \"common\": {\n      \"version\": \"Version\",\n      \"invalid_configuration\": \"Invalid configuration\",\n      \"show_warning\": \"Show Warning\",\n      \"show_error\": \"Show Error\"\n    }\n  }"
  },
  {
    "path": "src/translations/languages/en.json",
    "content": ""
  },
  {
    "path": "src/translations/localize.ts",
    "content": "import * as en from './languages/en.json';\nimport * as nb from './languages/de.json';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst languages: any = {\n  en,\n  nb,\n};\n\nexport function localize(string: string, search = '', replace = ''): string {\n  const lang = (localStorage.getItem('selectedLanguage') || 'en')\n    .replace(/['\"]+/g, '')\n    .replace('-', '_');\n\n  let translated: string;\n\n  try {\n    translated = string.split('.').reduce((o, i) => o[i], languages[lang]);\n  } catch (e) {\n    translated = string.split('.').reduce((o, i) => o[i], languages.en);\n  }\n\n  if (translated === undefined)\n    translated = string.split('.').reduce((o, i) => o[i], languages.en);\n\n  if (search !== '' && replace !== '') {\n    translated = translated.replace(search, replace);\n  }\n  return translated;\n}\n"
  },
  {
    "path": "src/types.ts",
    "content": "import { LovelaceCard, LovelaceCardEditor } from 'custom-card-helpers';\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'tesla-style-solar-power-card-editor': LovelaceCardEditor;\n    'hui-error-card': LovelaceCard;\n  }\n}\n"
  },
  {
    "path": "tesla-style-solar-power-card.js",
    "content": "!function(){\"use strict\";\n/*! *****************************************************************************\n    Copyright (c) Microsoft Corporation.\n\n    Permission to use, copy, modify, and/or distribute this software for any\n    purpose with or without fee is hereby granted.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\n    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\n    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n    PERFORMANCE OF THIS SOFTWARE.\n    ***************************************************************************** */function t(t,e,i,n){var s,o=arguments.length,r=o<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if(\"object\"==typeof Reflect&&\"function\"==typeof Reflect.decorate)r=Reflect.decorate(t,e,i,n);else for(var l=t.length-1;l>=0;l--)(s=t[l])&&(r=(o<3?s(r):o>3?s(e,i,r):s(e,i))||r);return o>3&&r&&Object.defineProperty(e,i,r),r\n/**\n     * @license\n     * Copyright 2019 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */}const e=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&\"adoptedStyleSheets\"in Document.prototype&&\"replace\"in CSSStyleSheet.prototype,i=Symbol(),n=new Map;class s{constructor(t,e){if(this._$cssResult$=!0,e!==i)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=t}get styleSheet(){let t=n.get(this.cssText);return e&&void 0===t&&(n.set(this.cssText,t=new CSSStyleSheet),t.replaceSync(this.cssText)),t}toString(){return this.cssText}}const o=(t,...e)=>{const n=1===t.length?t[0]:e.reduce(((e,i,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if(\"number\"==typeof t)return t;throw Error(\"Value passed to 'css' function must be a 'css' function result: \"+t+\". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.\")})(i)+t[n+1]),t[0]);return new s(n,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e=\"\";for(const i of t.cssRules)e+=i.cssText;return(t=>new s(\"string\"==typeof t?t:t+\"\",i))(e)})(t):t\n/**\n     * @license\n     * Copyright 2017 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */;var l;const a=window.trustedTypes,c=a?a.emptyScript:\"\",h=window.reactiveElementPolyfillSupport,u={toAttribute(t,e){switch(e){case Boolean:t=t?c:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},d=(t,e)=>e!==t&&(e==e||t==t),p={attribute:!0,type:String,converter:u,reflect:!1,hasChanged:d};class _ extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(t){var e;null!==(e=this.l)&&void 0!==e||(this.l=[]),this.l.push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const n=this._$Eh(i,e);void 0!==n&&(this._$Eu.set(n,i),t.push(n))})),t}static createProperty(t,e=p){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i=\"symbol\"==typeof t?Symbol():\"__\"+t,n=this.getPropertyDescriptor(t,i,e);void 0!==n&&Object.defineProperty(this.prototype,t,n)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(n){const s=this[t];this[e]=n,this.requestUpdate(t,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||p}static finalize(){if(this.hasOwnProperty(\"finalized\"))return!1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),this.elementProperties=new Map(t.elementProperties),this._$Eu=new Map,this.hasOwnProperty(\"properties\")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eh(t,e){const i=e.attribute;return!1===i?void 0:\"string\"==typeof i?i:\"string\"==typeof t?t.toLowerCase():void 0}o(){var t;this._$Ep=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(t=this.constructor.l)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$Eg)&&void 0!==e?e:this._$Eg=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$Eg)||void 0===e||e.splice(this._$Eg.indexOf(t)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Et.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const i=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,i)=>{e?t.adoptedStyleSheets=i.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):i.forEach((e=>{const i=document.createElement(\"style\"),n=window.litNonce;void 0!==n&&i.setAttribute(\"nonce\",n),i.textContent=e.cssText,t.appendChild(i)}))})(i,this.constructor.elementStyles),i}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ES(t,e,i=p){var n,s;const o=this.constructor._$Eh(t,i);if(void 0!==o&&!0===i.reflect){const r=(null!==(s=null===(n=i.converter)||void 0===n?void 0:n.toAttribute)&&void 0!==s?s:u.toAttribute)(e,i.type);this._$Ei=t,null==r?this.removeAttribute(o):this.setAttribute(o,r),this._$Ei=null}}_$AK(t,e){var i,n,s;const o=this.constructor,r=o._$Eu.get(t);if(void 0!==r&&this._$Ei!==r){const t=o.getPropertyOptions(r),l=t.converter,a=null!==(s=null!==(n=null===(i=l)||void 0===i?void 0:i.fromAttribute)&&void 0!==n?n:\"function\"==typeof l?l:null)&&void 0!==s?s:u.fromAttribute;this._$Ei=r,this[r]=a(e,t.type),this._$Ei=null}}requestUpdate(t,e,i){let n=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||d)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$Ei!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):n=!1),!this.isUpdatePending&&n&&(this._$Ep=this._$E_())}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((t,e)=>this[e]=t)),this._$Et=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$Eg)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$EU()}catch(t){throw e=!1,this._$EU(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$Eg)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$ES(e,this[e],t))),this._$EC=void 0),this._$EU()}updated(t){}firstUpdated(t){}}\n/**\n     * @license\n     * Copyright 2017 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */\nvar y;_.finalized=!0,_.elementProperties=new Map,_.elementStyles=[],_.shadowRootOptions={mode:\"open\"},null==h||h({ReactiveElement:_}),(null!==(l=globalThis.reactiveElementVersions)&&void 0!==l?l:globalThis.reactiveElementVersions=[]).push(\"1.3.1\");const g=globalThis.trustedTypes,v=g?g.createPolicy(\"lit-html\",{createHTML:t=>t}):void 0,m=`lit$${(Math.random()+\"\").slice(9)}$`,f=\"?\"+m,b=`<${f}>`,$=document,w=(t=\"\")=>$.createComment(t),x=t=>null===t||\"object\"!=typeof t&&\"function\"!=typeof t,A=Array.isArray,C=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,E=/-->/g,S=/>/g,R=/>|[ \t\\n\f\\r](?:([^\\s\"'>=/]+)([ \t\\n\f\\r]*=[ \t\\n\f\\r]*(?:[^ \t\\n\f\\r\"'`<>=]|(\"|')|))|$)/g,B=/'/g,M=/\"/g,k=/^(?:script|style|textarea|title)$/i,O=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),P=Symbol.for(\"lit-noChange\"),U=Symbol.for(\"lit-nothing\"),T=new WeakMap,H=$.createTreeWalker($,129,null,!1),N=(t,e)=>{const i=t.length-1,n=[];let s,o=2===e?\"<svg>\":\"\",r=C;for(let e=0;e<i;e++){const i=t[e];let l,a,c=-1,h=0;for(;h<i.length&&(r.lastIndex=h,a=r.exec(i),null!==a);)h=r.lastIndex,r===C?\"!--\"===a[1]?r=E:void 0!==a[1]?r=S:void 0!==a[2]?(k.test(a[2])&&(s=RegExp(\"</\"+a[2],\"g\")),r=R):void 0!==a[3]&&(r=R):r===R?\">\"===a[0]?(r=null!=s?s:C,c=-1):void 0===a[1]?c=-2:(c=r.lastIndex-a[2].length,l=a[1],r=void 0===a[3]?R:'\"'===a[3]?M:B):r===M||r===B?r=R:r===E||r===S?r=C:(r=R,s=void 0);const u=r===R&&t[e+1].startsWith(\"/>\")?\" \":\"\";o+=r===C?i+b:c>=0?(n.push(l),i.slice(0,c)+\"$lit$\"+i.slice(c)+m+u):i+m+(-2===c?(n.push(void 0),e):u)}const l=o+(t[i]||\"<?>\")+(2===e?\"</svg>\":\"\");if(!Array.isArray(t)||!t.hasOwnProperty(\"raw\"))throw Error(\"invalid template strings array\");return[void 0!==v?v.createHTML(l):l,n]};class D{constructor({strings:t,_$litType$:e},i){let n;this.parts=[];let s=0,o=0;const r=t.length-1,l=this.parts,[a,c]=N(t,e);if(this.el=D.createElement(a,i),H.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=H.nextNode())&&l.length<r;){if(1===n.nodeType){if(n.hasAttributes()){const t=[];for(const e of n.getAttributeNames())if(e.endsWith(\"$lit$\")||e.startsWith(m)){const i=c[o++];if(t.push(e),void 0!==i){const t=n.getAttribute(i.toLowerCase()+\"$lit$\").split(m),e=/([.?@])?(.*)/.exec(i);l.push({type:1,index:s,name:e[2],strings:t,ctor:\".\"===e[1]?q:\"?\"===e[1]?j:\"@\"===e[1]?F:V})}else l.push({type:6,index:s})}for(const e of t)n.removeAttribute(e)}if(k.test(n.tagName)){const t=n.textContent.split(m),e=t.length-1;if(e>0){n.textContent=g?g.emptyScript:\"\";for(let i=0;i<e;i++)n.append(t[i],w()),H.nextNode(),l.push({type:2,index:++s});n.append(t[e],w())}}}else if(8===n.nodeType)if(n.data===f)l.push({type:2,index:s});else{let t=-1;for(;-1!==(t=n.data.indexOf(m,t+1));)l.push({type:7,index:s}),t+=m.length-1}s++}}static createElement(t,e){const i=$.createElement(\"template\");return i.innerHTML=t,i}}function W(t,e,i=t,n){var s,o,r,l;if(e===P)return e;let a=void 0!==n?null===(s=i._$Cl)||void 0===s?void 0:s[n]:i._$Cu;const c=x(e)?void 0:e._$litDirective$;return(null==a?void 0:a.constructor)!==c&&(null===(o=null==a?void 0:a._$AO)||void 0===o||o.call(a,!1),void 0===c?a=void 0:(a=new c(t),a._$AT(t,i,n)),void 0!==n?(null!==(r=(l=i)._$Cl)&&void 0!==r?r:l._$Cl=[])[n]=a:i._$Cu=a),void 0!==a&&(e=W(t,a._$AS(t,e.values),a,n)),e}class L{constructor(t,e){this.v=[],this._$AN=void 0,this._$AD=t,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}p(t){var e;const{el:{content:i},parts:n}=this._$AD,s=(null!==(e=null==t?void 0:t.creationScope)&&void 0!==e?e:$).importNode(i,!0);H.currentNode=s;let o=H.nextNode(),r=0,l=0,a=n[0];for(;void 0!==a;){if(r===a.index){let e;2===a.type?e=new I(o,o.nextSibling,this,t):1===a.type?e=new a.ctor(o,a.name,a.strings,this,t):6===a.type&&(e=new K(o,this,t)),this.v.push(e),a=n[++l]}r!==(null==a?void 0:a.index)&&(o=H.nextNode(),r++)}return s}m(t){let e=0;for(const i of this.v)void 0!==i&&(void 0!==i.strings?(i._$AI(t,i,e),e+=i.strings.length-2):i._$AI(t[e])),e++}}class I{constructor(t,e,i,n){var s;this.type=2,this._$AH=U,this._$AN=void 0,this._$AA=t,this._$AB=e,this._$AM=i,this.options=n,this._$Cg=null===(s=null==n?void 0:n.isConnected)||void 0===s||s}get _$AU(){var t,e;return null!==(e=null===(t=this._$AM)||void 0===t?void 0:t._$AU)&&void 0!==e?e:this._$Cg}get parentNode(){let t=this._$AA.parentNode;const e=this._$AM;return void 0!==e&&11===t.nodeType&&(t=e.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,e=this){t=W(this,t,e),x(t)?t===U||null==t||\"\"===t?(this._$AH!==U&&this._$AR(),this._$AH=U):t!==this._$AH&&t!==P&&this.$(t):void 0!==t._$litType$?this.T(t):void 0!==t.nodeType?this.k(t):(t=>{var e;return A(t)||\"function\"==typeof(null===(e=t)||void 0===e?void 0:e[Symbol.iterator])})(t)?this.S(t):this.$(t)}A(t,e=this._$AB){return this._$AA.parentNode.insertBefore(t,e)}k(t){this._$AH!==t&&(this._$AR(),this._$AH=this.A(t))}$(t){this._$AH!==U&&x(this._$AH)?this._$AA.nextSibling.data=t:this.k($.createTextNode(t)),this._$AH=t}T(t){var e;const{values:i,_$litType$:n}=t,s=\"number\"==typeof n?this._$AC(t):(void 0===n.el&&(n.el=D.createElement(n.h,this.options)),n);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===s)this._$AH.m(i);else{const t=new L(s,this),e=t.p(this.options);t.m(i),this.k(e),this._$AH=t}}_$AC(t){let e=T.get(t.strings);return void 0===e&&T.set(t.strings,e=new D(t)),e}S(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,n=0;for(const s of t)n===e.length?e.push(i=new I(this.A(w()),this.A(w()),this,this.options)):i=e[n],i._$AI(s),n++;n<e.length&&(this._$AR(i&&i._$AB.nextSibling,n),e.length=n)}_$AR(t=this._$AA.nextSibling,e){var i;for(null===(i=this._$AP)||void 0===i||i.call(this,!1,!0,e);t&&t!==this._$AB;){const e=t.nextSibling;t.remove(),t=e}}setConnected(t){var e;void 0===this._$AM&&(this._$Cg=t,null===(e=this._$AP)||void 0===e||e.call(this,t))}}class V{constructor(t,e,i,n,s){this.type=1,this._$AH=U,this._$AN=void 0,this.element=t,this.name=e,this._$AM=n,this.options=s,i.length>2||\"\"!==i[0]||\"\"!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=U}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,n){const s=this.strings;let o=!1;if(void 0===s)t=W(this,t,e,0),o=!x(t)||t!==this._$AH&&t!==P,o&&(this._$AH=t);else{const n=t;let r,l;for(t=s[0],r=0;r<s.length-1;r++)l=W(this,n[i+r],e,r),l===P&&(l=this._$AH[r]),o||(o=!x(l)||l!==this._$AH[r]),l===U?t=U:t!==U&&(t+=(null!=l?l:\"\")+s[r+1]),this._$AH[r]=l}o&&!n&&this.C(t)}C(t){t===U?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,null!=t?t:\"\")}}class q extends V{constructor(){super(...arguments),this.type=3}C(t){this.element[this.name]=t===U?void 0:t}}const z=g?g.emptyScript:\"\";class j extends V{constructor(){super(...arguments),this.type=4}C(t){t&&t!==U?this.element.setAttribute(this.name,z):this.element.removeAttribute(this.name)}}class F extends V{constructor(t,e,i,n,s){super(t,e,i,n,s),this.type=5}_$AI(t,e=this){var i;if((t=null!==(i=W(this,t,e,0))&&void 0!==i?i:U)===P)return;const n=this._$AH,s=t===U&&n!==U||t.capture!==n.capture||t.once!==n.once||t.passive!==n.passive,o=t!==U&&(n===U||s);s&&this.element.removeEventListener(this.name,this,n),o&&this.element.addEventListener(this.name,this,t),this._$AH=t}handleEvent(t){var e,i;\"function\"==typeof this._$AH?this._$AH.call(null!==(i=null===(e=this.options)||void 0===e?void 0:e.host)&&void 0!==i?i:this.element,t):this._$AH.handleEvent(t)}}class K{constructor(t,e,i){this.element=t,this.type=6,this._$AN=void 0,this._$AM=e,this.options=i}get _$AU(){return this._$AM._$AU}_$AI(t){W(this,t)}}const G=window.litHtmlPolyfillSupport;\n/**\n     * @license\n     * Copyright 2017 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */\nvar Z,J;null==G||G(D,I),(null!==(y=globalThis.litHtmlVersions)&&void 0!==y?y:globalThis.litHtmlVersions=[]).push(\"2.2.1\");class Y extends _{constructor(){super(...arguments),this.renderOptions={host:this},this._$Dt=void 0}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Dt=((t,e,i)=>{var n,s;const o=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:e;let r=o._$litPart$;if(void 0===r){const t=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:null;o._$litPart$=r=new I(e.insertBefore(w(),t),t,void 0,null!=i?i:{})}return r._$AI(t),r})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Dt)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Dt)||void 0===t||t.setConnected(!1)}render(){return P}}Y.finalized=!0,Y._$litElement$=!0,null===(Z=globalThis.litElementHydrateSupport)||void 0===Z||Z.call(globalThis,{LitElement:Y});const Q=globalThis.litElementPolyfillSupport;null==Q||Q({LitElement:Y}),(null!==(J=globalThis.litElementVersions)&&void 0!==J?J:globalThis.litElementVersions=[]).push(\"3.2.0\");\n/**\n     * @license\n     * Copyright 2017 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */\nconst X=(t,e)=>\"method\"===e.kind&&e.descriptor&&!(\"value\"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:\"field\",key:Symbol(),placement:\"own\",descriptor:{},originalKey:e.key,initializer(){\"function\"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function tt(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):X(t,e)\n/**\n     * @license\n     * Copyright 2021 Google LLC\n     * SPDX-License-Identifier: BSD-3-Clause\n     */}var et;null===(et=window.HTMLSlotElement)||void 0===et||et.prototype.assignedElements;class it{constructor(t,e){this.speed=0,this.startPosition=0,this.currentPosition=0,this.currentDelta=0,this.maxPosition=30,this.unitOfMeasurement=\"\",this.accText=\"\",this.accTextclassName=\"accText\",this.entity=\"\",this.color=\"stroke:var(--info-color)\",this.circleColor=\"var(--primary-color)\",this.prevTimestamp=0,this.accTextElement=null,this.entity=t,this.entitySlot=e,this.value=0}setValueAndUnitOfMeasurement(t,e){if(void 0===t)return void(this.value=0);if(void 0===e)return void(this.value=t);const i=parseFloat(t);switch(e){case\"W\":case\"w\":case\"kW\":this.value=i,\"kW\"===e&&(this.value*=1e3),this.unitOfMeasurement=\"W\",this.value=Math.round(this.value);break;case\"%\":this.value=i,this.unitOfMeasurement=e;break;default:this.value=t,this.unitOfMeasurement=e}}setSpeed(t){if(this.speed=0,0===Math.abs(this.value))return;let e;e=void 0===t||t>1||t<=0?it.SPEEDFACTOR:t,this.speed=e*this.value/1e3}}it.SPEEDFACTOR=.04;class nt{constructor(){this.mainValue=0,this.clickEntitySlot=null,this.clickEntityHassState=null,this.noEntitiesWithValueFound=!0}}class st{constructor(t,e){this.teslaCard=t,this.solarCardElements=t.solarCardElements,this.pxRate=t.pxRate,this.hass=e}writeBubbleDiv(t){return t.noEntitiesWithValueFound?O``:O` <div class=\"acc_td ${t.cssSelector}\">\n      <div\n        class=\"acc_container ${t.clickEntitySlot}\"\n        style=\"${\"width:\"+9*this.pxRate+\"px; height: \"+9*this.pxRate+\"px; padding:\"+5*this.pxRate+\"px;\"}\"\n        @click=\"${()=>this._handleClick(t.clickEntityHassState)}\"\n      >\n        ${null!==t.extraValue?O` <div\n              class=\"acc_text_extra\"\n              style=\"font-size:${3*this.pxRate+\"px\"};\n                        top: ${1*this.pxRate+\"px\"};\n                        width: ${10*this.pxRate+\"px\"};\"\n            >${t.extraValue} ${t.extraUnitOfMeasurement}\n            </div>`:O``}\n        <ha-icon class=\"acc_icon\" icon=\"${t.icon}\"></ha-icon>\n        <div class=\"acc_text\" style=\"font-size:${3*this.pxRate+\"px\"}; margin-top:${-.5*this.pxRate+\"px\"}; width: ${10*this.pxRate+\"px\"}\">\n          ${t.mainValue} ${t.mainUnitOfMeasurement}\n        </div>\n      </div>\n    </div>`}writeBatteryBubbleDiv(t){return void 0!==t.extraValue&&(\"mdi:battery-medium\"!==t.icon&&\"mdi:battery\"!==t.icon||(t.icon=this.getBatteryIcon(parseFloat(t.extraValue),t.mainValue))),this.writeBubbleDiv(t)}getBatteryIcon(t,e){let i=t;t<=5&&(i=0);const n=10*Math.ceil(i/10);let s=\"-\"+n.toString(),o=\"-charging\";return e<=0&&(o=\"\"),100===n&&(s=\"\"),n<=5&&(s=\"-outline\"),\"mdi:battery\"+o+s}writeAppliancePowerLineAndCircle(t,e){if(null==this.solarCardElements.get(\"appliance\"+t+\"_consumption_entity\"))return O``;let i;return i=1===t?\"top:\"+22.5*this.pxRate+\"px;\":\"bottom:\"+15*this.pxRate+\"px;\",O` <div\n      class=\"acc_line acc_appliance${t}_line\"\n      style=\"\n        height:${12*this.pxRate-5*(t-1)+\"px\"}\n        width:10px};\n        right:${9.5*this.pxRate+10+\"px\"};\n        ${i}\n        position:absolute\"\n    >\n      <svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox='${\"0 0 \"+(12*this.pxRate-5*(t-1))+\" \"+(12*this.pxRate-5*(t-1))}'\n        preserveAspectRatio=\"xMinYMax slice\"\n        style=\"height:${12*this.pxRate-5*(t-1)+\"px\"};width:10px}\"\n        class=\"acc_appliance${t}_line_svg\"\n      >\n        ${this.writeCircleAndLine(\"appliance\"+t+\"_consumption_entity\",e)}\n      </svg>\n    </div>`}writeCircleAndLine(t,e){const i=this.solarCardElements.get(t);return null==i?O``:O`<svg>\n      <circle r=\"4\" cx=\"${i.startPosition.toString()}\" cy=\"4\" fill=\"${i.color}\" id=\"${t+\"_circle\"}\"></circle>\n      <path d=\"${e}\" id=\"${t+\"_line\"}\"></path>\n    </svg>`}_handleClick(t){if(null==t)return;const e=new Event(\"hass-more-info\",{bubbles:!0,cancelable:!0,composed:!0});e.detail={entityId:t.entity_id},null!=this.teslaCard.shadowRoot&&this.teslaCard.shadowRoot.dispatchEvent(e)}}class ot{static changeStylesDependingOnWidth(t,e,i,n){if(\"complete\"!==document.readyState||n===i)return n;if(null==t.shadowRoot)return n;const s=t.shadowRoot.querySelector(\"#tesla-style-solar-power-card\");if(null==s)return n;i<200&&(i=250);const o=i/100,r=function(t,e,i){const n=s.querySelector(t);null!==n&&(n.style[e]=i)};r(\".acc_left\",\"top\",12*o+\"px\"),r(\".acc_right\",\"top\",12*o+\"px\"),s.querySelectorAll(\".acc_container\").forEach(((t,e,i)=>{const n=i[e];n.style.height=9*o+\"px\",n.style.width=9*o+\"px\",n.style.padding=5*o+\"px\"})),s.querySelectorAll(\"ha-icon\").forEach(((t,e,i)=>{var n;const s=null===(n=i[e].shadowRoot)||void 0===n?void 0:n.querySelector(\"ha-svg-icon\");null!=s&&(s.style.height=9*o+\"px\",s.style.width=9*o+\"px\")})),s.querySelectorAll(\".acc_text\").forEach((t=>{t.style[\"font-size\"]=3*o+\"px\",t.style[\"margin-top\"]=-.5*o+\"px\",t.style.width=10*o+\"px\"})),s.querySelectorAll(\".acc_text_extra\").forEach((t=>{t.style[\"font-size\"]=3*o+\"px\",t.style.top=1*o+\"px\",t.style.width=10*o+\"px\"})),r(\".power_lines\",\"height\",42*o+\"px\"),r(\".power_lines\",\"width\",42*o+\"px\"),r(\".power_lines\",\"top\",0*o+\"px\"),r(\".power_lines\",\"left\",28*o+\"px\"),r(\".power_lines svg\",\"width\",42*o+\"px\"),r(\".power_lines svg\",\"height\",42*o+\"px\"),r(\".power_lines svg\",\"viewBox\",\"0 0 \"+42*o+\" \"+42*o);let l=s.querySelector(\".power_lines svg\");null!==l&&l.setAttribute(\"viewBox\",\"0 0 \"+42*o+\" \"+42*o);const a=22*o;r(\"#generation_to_house_entity_line\",\"d\",\"M\"+(a-o)+\",0 C\"+(a-o)+\",\"+a+\" \"+(a-o)+\",\"+a+\" \"+2*a+\",\"+a),r(\"#grid_feed_in_entity_line\",\"d\",\"M\"+(a-o)+\",0 C\"+(a-o)+\",\"+a+\" \"+(a-o)+\",\"+a+\" 0,\"+a),r(\"#grid_to_house_entity_line\",\"d\",\"M0,\"+a+\" C\"+a+\",\"+a+\" \"+a+\",\"+a+\" \"+2*a+\",\"+a),r(\"#grid_to_battery_entity_line\",\"d\",\"M0,\"+a+\" C\"+a+\",\"+a+\" \"+a+\",\"+a+\" \"+a+\",\"+2*a),r(\"#battery_to_house_entity_line\",\"d\",\"M\"+(a-o)+\",\"+2*a+\" C\"+(a-o)+\",\"+a+\" \"+(a-o)+\",\"+a+\" \"+2*a+\",\"+a),r(\"#generation_to_battery_entity_line\",\"d\",\"M\"+(a-o)+\",0 C\"+(a-o)+\",0 \"+(a-o)+\",\"+2*a+\" \"+(a-o)+\",\"+2*a),[1,2].forEach((t=>{r(\".acc_appliance\"+t+\"_line svg\",\"viewBox\",\"0 0 \"+(12*o-5*(t-1))+\" \"+(12*o-5*(t-1))),r(\".acc_appliance\"+t+\"_line\",\"right\",9.5*o+10+\"px\"),r(\".acc_appliance\"+t+\"_line\",\"width\",\"10px\"),r(\".acc_appliance\"+t+\"_line\",\"height\",12*o-5*(t-1)+\"px\"),r(\".acc_appliance\"+t+\"_line svg\",\"width\",\"10px\"),r(\".acc_appliance\"+t+\"_line svg\",\"height\",12*o-5*(t-1)+\"px\"),l=s.querySelector(\".acc_appliance\"+t+\"_line_svg\"),null!==l&&l.setAttribute(\"viewBox\",\"0 0 \"+(12*o-5*(t-1))+\" \"+(12*o-5*(t-1)));null===s.querySelector(\".generation_entity\")&&1===t&&null!==l&&r(\".acc_center_container\",\"margin-top\",19*o+\"px\");null===s.querySelector(\".battery_entity\")&&2===t&&null!==l&&r(\".acc_center_container\",\"margin-bottom\",19*o+\"px\")}));return null===s.querySelector(\".grid_entity\")&&(r(\".generation_entity\",\"margin\",\"0px\"),r(\".battery_entity\",\"margin\",\"0px\"),r(\".power_lines\",\"width\",30*o+\"px\"),l=s.querySelector(\".power_lines svg\"),null!==l&&l.setAttribute(\"viewBox\",12*o+\" 0 \"+42*o+\" \"+42*o)),r(\".acc_appliance1\",\"top\",\"10px\"),r(\".acc_appliance1_line\",\"top\",19*o+12+\"px\"),r(\".acc_appliance2\",\"bottom\",\"10px\"),r(\".acc_appliance2_line\",\"bottom\",19*o+12+\"px\"),i}}window.customCards=window.customCards||[],window.customCards.push({type:\"tesla-style-solar-power-card\",name:\"Tesla Style Solar Power Card\",description:\"A Solar Power Visualization with svg paths that mimmicks the powerwall app of tesla 2\"});class rt extends Y{constructor(){super(...arguments),this.solarCardElements=new Map,this.oldWidth=100,this.pxRate=4,this.htmlWriter=new st(this,this.hass),this.title=\"Hey there\",this.counter=5,this.error=\"\"}__increment(){this.counter+=1}setConfig(t){if(t.test_gui,this.config={...t},null==this.config.grid_icon&&(this.config.grid_icon=\"mdi:transmission-tower\"),null==this.config.generation_icon&&(this.config.generation_icon=\"mdi:solar-panel-large\"),null==this.config.house_icon&&(this.config.house_icon=\"mdi:home\"),null==this.config.battery_icon&&(this.config.battery_icon=\"mdi:battery-medium\"),null==this.config.appliance1_icon&&(this.config.appliance1_icon=\"mdi:car-sports\"),null==this.config.appliance2_icon&&(this.config.appliance2_icon=\"mdi:air-filter\"),null==this.config.speed_factor&&(this.config.speed_factor=.04),this.createSolarCardElements(),!this.config.energy_flow_diagramm){const t=this;setInterval(this.animateCircles,15,t)}}createSolarCardElements(){Object.keys(this.config).forEach((t=>{if(null!=this.config[t]&&t.indexOf(\"_entity\")>5){const e=this.config[t].toString();this.solarCardElements.set(t,new it(e,t))}}))}getCardSize(){return 5}static getStubConfig(){return{}}async firstUpdated(){await new Promise((t=>setTimeout(t,0)));const t=this.getBoundingClientRect().width;this.oldWidth=ot.changeStylesDependingOnWidth(this,this.solarCardElements,t,this.oldWidth)}connectedCallback(){super.connectedCallback(),this.redraw=this.redraw.bind(this),window.addEventListener(\"resize\",this.redraw)}shouldUpdate(t){let e;e=this,this.config.energy_flow_diagramm||requestAnimationFrame((t=>{e.updateAllCircles(t)})),e=this;let i=!0;return Array.from(t.keys()).some((e=>{const n=t.get(e);return\"hass\"===e&&n&&(i=i&&this.sensorChangeDetected(n)),!i})),i}sensorChangeDetected(t){let e=!1;return this.solarCardElements.forEach(((i,n)=>{void 0!==this.hass.states[this.config[n]]&&this.hass.states[this.config[n]].state!==t.states[this.config[n]].state&&(e=!0)})),e}async performUpdate(){this.error=\"\",this.solarCardElements.forEach((t=>{try{t.setValueAndUnitOfMeasurement(this.hass.states[t.entity].state,this.hass.states[t.entity].attributes.unit_of_measurement),t.setSpeed(this.config.speed_factor)}catch(e){this.error+=\" Configured '\"+t.entity+\"' entity was not found. \"}})),this.config.energy_flow_diagramm&&this.setEnergyFlowDiagramm(),this.config.change_house_bubble_color_with_flow&&this.colourHouseBubbleDependingOnHighestInput(),super.performUpdate()}render(){if(\"\"!==this.error)return this._showError();let t,e=this.getBoundingClientRect().width;e<200&&(e=250),this.pxRate=e/100,t=void 0!==this.config.show_gap&&this.config.show_gap?2*this.pxRate:0;const i=22*this.pxRate;return O`\n      <ha-card .header=${this.config.name} tabindex=\"0\">\n        <div id=\"tesla-style-solar-power-card\">\n          ${this.writeGenerationIconBubble()}\n          <div class=\"acc_center\">\n            <div class=\"acc_center_container\">\n              ${this.writeGridIconBubble()}\n              <div\n                class=\"acc_line power_lines\"\n                style=\"\n                height:${42*this.pxRate+\"px\"};\n                width:${42*this.pxRate+\"px\"};\n                top:${0*this.pxRate+\"px\"};\n                left:${28*this.pxRate+\"px\"}\"\n              >\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  viewBox=\"${\"0 0 \"+42*this.pxRate+\" \"+42*this.pxRate}\"\n                  preserveAspectRatio=\"xMinYMax slice\"\n                  style=\"height:${42*this.pxRate+\"px\"};width:${42*this.pxRate+\"px\"}\"\n                >\n                  ${this.htmlWriter.writeCircleAndLine(\"generation_to_house_entity\",\"M\"+(i-this.pxRate+t)+\",0C\"+(i-this.pxRate+t)+\",\"+(i-t)+\" \"+(i-this.pxRate+t)+\",\"+(i-t)+\" \"+2*i+\",\"+(i-t))}\n                  ${this.htmlWriter.writeCircleAndLine(\"grid_to_house_entity\",\"M0,\"+i+\" C\"+(i-this.pxRate)+\",\"+i+\" \"+(i-this.pxRate)+\",\"+i+\" \"+2*(i-this.pxRate)+\",\"+i)}\n                  ${this.htmlWriter.writeCircleAndLine(\"generation_to_grid_entity\",\"M\"+(i-this.pxRate-t)+\",0 C\"+(i-this.pxRate-t)+\",\"+(i-t)+\" \"+(i-this.pxRate-t)+\",\"+(i-t)+\" 0,\"+(i-t))}\n                  ${this.htmlWriter.writeCircleAndLine(\"grid_to_battery_entity\",\"M0,\"+(i+t)+\" C\"+(i-this.pxRate-t)+\",\"+(i+t)+\" \"+(i-this.pxRate-t)+\",\"+(i+t)+\" \"+(i-this.pxRate-t)+\",\"+2*i)}\n                  ${this.htmlWriter.writeCircleAndLine(\"battery_to_grid_entity\",\"M\"+(i-this.pxRate-t)+\",\"+2*i+\" C\"+(i-this.pxRate-t)+\",\"+(i+t)+\" \"+(i-this.pxRate-t)+\",\"+(i+t)+\" 0,\"+(i+t))}\n                  ${this.htmlWriter.writeCircleAndLine(\"generation_to_battery_entity\",\"M\"+(i-this.pxRate)+\",0 C\"+(i-this.pxRate)+\",0 \"+(i-this.pxRate)+\",\"+2*i+\" \"+(i-this.pxRate)+\",\"+2*i)}\n                  ${this.htmlWriter.writeCircleAndLine(\"battery_to_house_entity\",\"M\"+(i-this.pxRate+t)+\",\"+2*i+\" C\"+(i-this.pxRate+t)+\",\"+(i+t)+\" \"+(i-this.pxRate+t)+\",\"+(i+t)+\" \"+2*i+\",\"+(i+t))}\n                </svg>\n              </div>\n\n              ${this.writeHouseIconBubble()} ${this.writeApplianceIconBubble(1)}\n              ${this.htmlWriter.writeAppliancePowerLineAndCircle(1,\"M5,\"+12*this.pxRate+\" C5,\"+12*this.pxRate+\" 5,0 5,0\")}\n              ${this.writeApplianceIconBubble(2)}\n              ${this.htmlWriter.writeAppliancePowerLineAndCircle(2,\"M5,0 C5,0 5,\"+11*this.pxRate+\" 5,\"+11*this.pxRate)}\n            </div>\n          </div>\n          <div class=\"acc_bottom\">${this.writeBatteryIconBubble()}</div>\n        </div>\n      </ha-card>\n    `}writeGenerationIconBubble(){const t=this.calculateIconBubbleData([\"generation_to_grid_entity\",\"generation_to_house_entity\",\"generation_to_battery_entity\"],\"generation_entity\",\"generation_extra_entity\");return t.cssSelector=\"acc_top\",t.icon=this.config.generation_icon,this.htmlWriter.writeBatteryBubbleDiv(t)}writeGridIconBubble(){const t=this.calculateIconBubbleData([\"-generation_to_grid_entity\",\"grid_to_house_entity\",\"-battery_to_grid_entity\",\"grid_to_battery_entity\"],\"grid_entity\",\"grid_extra_entity\");return t.cssSelector=\"acc_left\",t.icon=this.config.grid_icon,this.htmlWriter.writeBatteryBubbleDiv(t)}writeHouseIconBubble(){let t;t=this.config.house_without_appliances_values?[\"generation_to_house_entity\",\"grid_to_house_entity\",\"battery_to_house_entity\",\"-appliance1_consumption_entity\",\"-appliance2_consumption_entity\"]:[\"generation_to_house_entity\",\"grid_to_house_entity\",\"battery_to_house_entity\"];const e=this.calculateIconBubbleData(t,\"house_entity\",\"house_extra_entity\");return e.cssSelector=\"acc_right\",e.icon=this.config.house_icon,this.htmlWriter.writeBatteryBubbleDiv(e)}writeBatteryIconBubble(){const t=this.calculateIconBubbleData([\"generation_to_battery_entity\",\"grid_to_battery_entity\",\"-battery_to_house_entity\",\"-battery_to_grid_entity\"],\"battery_entity\",\"battery_extra_entity\");return t.cssSelector=\"acc_bottom\",t.icon=this.config.battery_icon,this.htmlWriter.writeBatteryBubbleDiv(t)}writeApplianceIconBubble(t){const e=[\"appliance\"+t+\"_consumption_entity\"],i=this.calculateIconBubbleData(e,\"appliance\"+t+\"_consumption_entity\",\"appliance\"+t+\"_extra_entity\");return i.cssSelector=\"acc_appliance\"+t,i.icon=this.config[\"appliance\"+t+\"_icon\"],this.htmlWriter.writeBatteryBubbleDiv(i)}calculateIconBubbleData(t,e=null,i=null){let n=!1;const s=new nt;if(s.clickEntitySlot=e,t.forEach((t=>{\"-\"===t.substring(0,1)&&(t=t.substring(1),n=!0);const e=this.solarCardElements.get(t);null!==e&&void 0!==(null==e?void 0:e.value)&&(s.noEntitiesWithValueFound=!1,s.mainValue=n?s.mainValue-(null==e?void 0:e.value):s.mainValue+(null==e?void 0:e.value),s.mainValue=(100*s.mainValue|0)/100,s.mainUnitOfMeasurement=null==e?void 0:e.unitOfMeasurement),n=!1})),null!==i){const t=this.solarCardElements.get(i);s.extraValue=null==t?void 0:t.value,s.extraUnitOfMeasurement=null==t?void 0:t.unitOfMeasurement}return null!==e&&(s.clickEntityHassState=this.hass.states[this.config[e]]),this.showKW(s.mainValue)&&(s.mainValue=this.roundValue(s.mainValue/1e3),s.mainUnitOfMeasurement=\"kW\"),s}showKW(t){return!this.config.show_w_not_kw&&!(void 0!==this.config.threshold_in_k&&Math.abs(t)<1e3*this.config.threshold_in_k)}roundValue(t){let e;return e=t>.1?(0|Math.round(10*(t+Number.EPSILON)))/10:(0|Math.round(100*(t+Number.EPSILON)))/100,e}animateCircles(t){requestAnimationFrame((e=>{t.updateAllCircles(e)}))}updateAllCircles(t){this.solarCardElements.forEach(((e,i)=>{const n=this.solarCardElements.get(i);void 0!==n&&this.updateOneCircle(t,n)}))}updateOneCircle(t,e){if(null==this.shadowRoot)return;const i=this.shadowRoot.querySelector(\"#tesla-style-solar-power-card\");if(null==i)return;if(e.line=i.querySelector(\"#\"+e.entitySlot+\"_line\"),null===e.line)return;const n=e.line.getTotalLength();if(isNaN(n))return;if(e.circle=i.querySelector(\"#\"+e.entitySlot+\"_circle\"),0===e.speed)return e.circle.setAttribute(\"visibility\",\"hidden\"),void(this.config.hide_inactive_lines&&e.line.setAttribute(\"visibility\",\"hidden\"));e.circle.setAttribute(\"visibility\",\"visible\"),this.config.hide_inactive_lines&&e.line.setAttribute(\"visibility\",\"visible\"),0===e.prevTimestamp&&(e.prevTimestamp=t,e.currentDelta=0),e.currentDelta+=Math.abs(e.speed)*(t-e.prevTimestamp);let s=e.currentDelta/n;e.speed>0?(s>=1||isNaN(s))&&(e.currentDelta=0,s=.01):(s=1-s,(s<=0||isNaN(s))&&(e.currentDelta=0,s=1));const o=e.line.getPointAtLength(n*s);e.circle.setAttributeNS(null,\"cx\",o.x.toString()),e.circle.setAttributeNS(null,\"cy\",o.y.toString()),e.prevTimestamp=t}colourHouseBubbleDependingOnHighestInput(){if(null==this.shadowRoot)return;const t=this.shadowRoot.querySelector(\"#tesla-style-solar-power-card\");if(null==t)return;let e=null,i=\"\";switch([\"generation_to_house_entity\",\"grid_to_house_entity\",\"battery_to_house_entity\"].forEach((t=>{const n=this.solarCardElements.get(t);null!==n&&void 0!==(null==n?void 0:n.value)&&(null==e||(null==n?void 0:n.value)>e.value)&&(i=t,e=n)})),i){case\"generation_to_house_entity\":this.colourBubble(\".house_entity\",t,\"warning\"),this.colourBubble(\".appliance1_consumption_entity\",t,\"warning\"),this.colourBubble(\".appliance2_consumption_entity\",t,\"warning\"),this.colourLineAndCircle(\"#appliance1_consumption_entity\",t,\"warning\"),this.colourLineAndCircle(\"#appliance2_consumption_entity\",t,\"warning\");break;case\"battery_to_house_entity\":this.colourBubble(\".house_entity\",t,\"success\"),this.colourBubble(\".appliance1_consumption_entity\",t,\"success\"),this.colourBubble(\".appliance2_consumption_entity\",t,\"success\"),this.colourLineAndCircle(\"#appliance1_consumption_entity\",t,\"success\"),this.colourLineAndCircle(\"#appliance2_consumption_entity\",t,\"success\");break;case\"grid_to_house_entity\":this.colourBubble(\".house_entity\",t,\"info\"),this.colourBubble(\".appliance1_consumption_entity\",t,\"info\"),this.colourBubble(\".appliance2_consumption_entity\",t,\"info\"),this.colourLineAndCircle(\"#appliance1_consumption_entity\",t,\"info\"),this.colourLineAndCircle(\"#appliance2_consumption_entity\",t,\"info\")}}colourBubble(t,e,i){const n=e.querySelector(t);null!==n&&(n.style.color=\"var(--\"+i+\"-color)\",n.style.border=\"1px solid var(--\"+i+\"-color)\")}colourLineAndCircle(t,e,i){const n=e.querySelector(t+\"_line\"),s=e.querySelector(t+\"_circle\");null!==n&&(n.style.stroke=\"var(--\"+i+\"-color)\",s.style.fill=\"var(--\"+i+\"-color)\")}setEnergyFlowDiagramm(){if(null==this.shadowRoot)return;const t=this.shadowRoot.querySelector(\"#tesla-style-solar-power-card\");null!=t&&this.solarCardElements.forEach(((e,i)=>{const n=this.solarCardElements.get(i);let s=1;if(null==t)return;const o=t.querySelector(\"#\"+i+\"_line\");if(null!=o&&void 0!==n){t.querySelector(\"#\"+i+\"_circle\").style.visibility=\"hidden\",void 0===this.config.energy_flow_diagramm_lines_factor&&(this.config.energy_flow_diagramm_lines_factor=2),s=\"W\"===(null==n?void 0:n.unitOfMeasurement.toUpperCase())?Math.floor((null==n?void 0:n.value)/100)/10*this.config.energy_flow_diagramm_lines_factor:Math.floor(10*(null==n?void 0:n.value))/10*this.config.energy_flow_diagramm_lines_factor,s<=.1&&0!==s&&(s=.1),o.style.strokeWidth=s+\"px\"}}))}redraw(t){if(this.hass&&this.config&&\"resize\"===t.type){const t=this.getBoundingClientRect().width;this.oldWidth=ot.changeStylesDependingOnWidth(this,this.solarCardElements,t,this.oldWidth)}}_showWarning(t){return O` <hui-warning>${t}</hui-warning> `}_showError(){return console.log(this.error),O`\n      <hui-warning\n        ><div>\n          ERROR:<br />\n          ${this.error}\n        </div></hui-warning\n      >\n    `}static get styles(){return o`\n    #tesla-style-solar-power-card{\n      margin:auto;\n      display:table;\n      padding: 10px;\n      position: relative;\n    }\n    .acc_container {\n        height: 40px;\n        width: 40px;\n        border: 1px solid black;\n        border-radius: 100px;\n        padding: 22px;\n        color: var(--primary-text-color);\n        border-color: var(--primary-text-color);\n        position:relative;\n        cursor:pointer;\n    }\n    .acc_icon {\n        --mdc-icon-size: 40px;\n    }\n    .acc_text,\n    .acc_text_extra {\n        text-align: center;\n        white-space: nowrap;\n    }\n    .acc_text_extra {\n      overflow: hidden;\n      position: absolute;\n    }\n    .acc_td {\n        vertical-align: top;\n    }\n    .acc_center .acc_td{\n      position:relative;\n    }\n    .acc_top .acc_container,\n    .acc_bottom .acc_container{\n      margin:auto;\n    }\n    .acc_center{\n      display:flex;\n    }\n    .acc_center_container{\n      display:inline-block;\n      margin: 0px auto;\n      margin-bottom:-5px;\n    }\n\n    .acc_right ,\n    .acc_left ,\n    .acc_line{\n      display:inline-block;\n      margin-right:-4px\n    }\n    .acc_left {\n      vertical-align: top;\n    }\n    .acc_right {\n      margin-right:0px;\n    }\n    #battery_to_house_entity_line,\n    #generation_to_house_entity_line,\n    #grid_to_house_entity_line,\n    #generation_to_battery_entity_line,\n    #grid_feed_in_entity_line,\n    #generation_to_grid_entity_line,\n    #battery_to_grid_entity_line,\n    #grid_to_battery_entity_line,\n    #appliance1_consumption_entity_line,\n    #appliance2_consumption_entity_line{\n      stroke:var(--info-color);\n      fill:none;\n      stroke-width:1;\n    }\n\n    .generation_entity {\n      border: 1px solid var(--warning-color);\n    }\n    .generation_entity .acc_icon,\n    .generation_entity{\n      color: var(--warning-color);\n    }\n    .house_entity{\n      border: 1px solid var(--info-color);\n    }\n    .appliance1_consumption_entity,\n    .appliance2_consumption_entity {\n      border: 1px solid var(--info-color);\n    }\n    .house_entity,\n    .appliance1_consumption_entity,\n    .appliance2_consumption_entity{\n      color: var(--info-color);\n    }\n    #generation_to_house_entity_line,\n    #generation_to_grid_entity_line,\n    #generation_to_battery_entity_line{\n      stroke:var(--warning-color);\n    }\n    #grid_to_battery_entity_circle,\n    #grid_to_house_entity_circle,\n    #appliance1_consumption_entity_circle,\n    #appliance2_consumption_entity_circle{\n      fill:var(--info-color);\n    }\n    #generation_to_house_entity_circle,\n    #generation_to_grid_entity_circle,\n    #generation_to_battery_entity_circle{\n      fill:var(--warning-color);\n    }\n    #battery_to_house_entity_line,\n    #battery_to_grid_entity_line{\n      stroke:var(--success-color);\n    }\n    #battery_to_house_entity_circle,\n    #battery_to_grid_entity_circle{\n      fill:var(--success-color);\n    }\n    .battery_extra_entity,\n    .battery_entity{\n      border: 1px solid var(--success-color);\n      color: var(--success-color);\n    }\n    .battery_extra_text{\n      position:absolute;\n      top:8px;\n    }\n    br.clear {\n      clear:both;\n    }\n    .power_lines svg{\n      transform: translateZ(0);\n      display:inline-block;\n    }\n    .acc_center .acc_td.acc_appliance1,\n    .acc_center .acc_td.acc_appliance2 {\n      position: absolute;\n      right: 10px;\n    `}}t([tt({attribute:!1})],rt.prototype,\"hass\",void 0),t([tt()],rt.prototype,\"config\",void 0),t([tt({attribute:!1})],rt.prototype,\"solarCardElements\",void 0),t([tt()],rt.prototype,\"oldWidth\",void 0),t([tt({type:String})],rt.prototype,\"title\",void 0),t([tt({type:Number})],rt.prototype,\"counter\",void 0),window.customElements.define(\"tesla-style-solar-power-card\",rt)}();\n"
  },
  {
    "path": "tesla-style-solar-power-card.ts",
    "content": "import { TeslaStyleSolarPowerCard } from './src/TeslaStyleSolarPowerCard.js';\n\nwindow.customElements.define('tesla-style-solar-power-card', TeslaStyleSolarPowerCard);\n"
  },
  {
    "path": "test/SensorElement.test.ts",
    "content": "import { expect } from '@open-wc/testing';\n\nimport { SensorElement } from '../src/models/SensorElement.js';\n// import { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\n\ndescribe('SensorElements test', () => {\n  it('should setSpeed with W', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.unitOfMeasurement = 'W';\n    selement.setSpeed(0.04);\n    expect(selement.speed).to.equal(0.00004);\n  });\n\n  it('should setSpeed with kW', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.unitOfMeasurement = 'KW';\n    selement.setSpeed(0.04);\n    expect(selement.speed).to.equal(0.00004);\n  });\n\n  it('should setSpeed with kW with factor 0.05', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.unitOfMeasurement = 'KW';\n    selement.setSpeed(0.05);\n    expect(selement.speed).to.equal(0.00005);\n  });\n\n  it('should setValueAndUnitOfMeasurement from kW rounded to 1 decimals', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.setValueAndUnitOfMeasurement('1.1111', 'kW');\n    expect(selement.value).to.equal(1111);\n  });\n\n  it('should setValueAndUnitOfMeasurement from 0.0111 kW to W', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.setValueAndUnitOfMeasurement('0.0111', 'kW');\n    expect(selement.value).to.equal(11);\n  });\n\n  it('should setValueAndUnitOfMeasurement from W rounded to two decimals get kW', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.setValueAndUnitOfMeasurement('1100.1', 'W');\n    expect(selement.value).to.equal(1100);\n  });\n\n  it('should setValueAndUnitOfMeasurement from W rounded to 1 decimals but get W', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.setValueAndUnitOfMeasurement('1111.111', 'W');\n    expect(selement.value).to.equal(1111);\n  });\n\n  it('should setValueAndUnitOfMeasurement from percentage', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.setValueAndUnitOfMeasurement('1', '%');\n    expect(selement.value).to.equal(1);\n  });\n\n  it('should setValueAndUnitOfMeasurement from undefined', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.setValueAndUnitOfMeasurement(undefined, '%');\n    expect(selement.value).to.equal(0);\n  });\n\n  it('should setValueAndUnitOfMeasurement from normal string without unit', () => {\n    const selement = new SensorElement('test_entity', 'solar_consumption');\n    selement.value = 1;\n    selement.setValueAndUnitOfMeasurement('on', undefined);\n    expect(selement.value).to.equal('on');\n  });\n});\n"
  },
  {
    "path": "test/battery.test.ts",
    "content": "import { expect, elementUpdated, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\n\ndescribe('TeslaStyleSolarPowerCard battery tests', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      house_entity: 'sensor.house_consumption',\n      battery_extra_entity: 'sensor.battery_charge',\n      battery_entity: 'sensor.battery_consumption',\n      battery_to_house_entity: 'sensor.battery_consumption',\n    };\n    hass = {\n      states: {\n        'sensor.house_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'House consumption',\n          },\n          entity_id: 'sensor.house_consumption',\n          state: '1300',\n        },\n        'sensor.battery_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'battery_consumption',\n          state: '1300',\n        },\n        'sensor.battery_charge': {\n          attributes: {\n            unit_of_measurement: '%',\n          },\n          entity_id: 'sensor.battery_charge',\n          state: '100',\n        },\n        'sensor.battery_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'sensor.battery_to_house',\n          state: '1300',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    // let iframe = document.createElement('iframe');\n    // document.body.appendChild(iframe);\n    // let div = document.createElement('div');\n    // document.body.\n    // console.log(\"document width \" + document.body.clientWidth);\n    // document.body.appendChild(card);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  const setBatteryState = async (state: string) => {\n    hass.states['sensor.battery_charge'].state = state;\n    card.setAttribute('hass', JSON.stringify(hass));\n    await elementUpdated(card);\n    await card.setConfig(config);\n  };\n\n  it('has house_entity, text and icon', async () => {\n    const houseEntity = teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined)\n      assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '1.3 kW'\n    );\n    expect(\n      houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:home');\n  });\n\n  it('has battery_entity, text and icon', async () => {\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(batteryEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '1.3 kW'\n    );\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '100 %'\n    );\n  });\n\n  it('has battery to house consumption line and circle', async () => {\n    // await setCardConsumingFromGrid();\n    const batteryToHouseLine = teslaCard?.querySelector(\n      '#battery_to_house_entity_line'\n    );\n    if (batteryToHouseLine === null || batteryToHouseLine === undefined) {\n      assert.fail('No battery_to_house_line element found');\n    }\n    const batteryToHouseCircle = teslaCard?.querySelector(\n      '#battery_to_house_entity_circle'\n    );\n    if (batteryToHouseCircle === null || batteryToHouseCircle === undefined) {\n      assert.fail('No batter_to_house_entity_circle element found');\n    }\n\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    expect(batteryToHouseLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or appliance icons', async () => {\n    const pvEntity = teslaCard?.querySelector('.pv_consumption_entity');\n    if (pvEntity !== null)\n      assert.fail('No pv_consumption_entity element found');\n\n    const gridEntity = teslaCard?.querySelector('.grid_entity');\n    if (gridEntity !== null) assert.fail('No battery_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('No appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('No appliance2_consumption_entity element found');\n  });\n\n  it('has battery at 90%', async () => {\n    await setBatteryState('90');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-90');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '90 %'\n    );\n  });\n\n  it('has battery at 83%', async () => {\n    await setBatteryState('83');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-90');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '83 %'\n    );\n  });\n\n  it('has battery at 73%', async () => {\n    await setBatteryState('73');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-80');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '73 %'\n    );\n  });\n\n  it('has battery at 65%', async () => {\n    await setBatteryState('65');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-70');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '65 %'\n    );\n  });\n\n  it('has battery at 15%', async () => {\n    await setBatteryState('15');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-20');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '15 %'\n    );\n  });\n\n  it('has battery at 6%', async () => {\n    await setBatteryState('6');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-10');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '6 %'\n    );\n  });\n\n  it('has battery at 5%', async () => {\n    await setBatteryState('5');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-outline');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '5 %'\n    );\n  });\n\n  it('has battery at 5%', async () => {\n    await setBatteryState('5');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-outline');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '5 %'\n    );\n  });\n});\n"
  },
  {
    "path": "test/batteryCharging.test.ts",
    "content": "import { expect, elementUpdated, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with defaultConfig', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      generation_to_battery_entity: 'sensor.battery_charging',\n      grid_to_battery_entity: 'sensor.grid_to_battery',\n      battery_extra_entity: 'sensor.battery_charge',\n      battery_entity: 'sensor.battery_consumption',\n    };\n    hass = {\n      states: {\n        'sensor.grid_to_battery': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'Grid to battery',\n          },\n          entity_id: 'sensor.grid_to_battery',\n          state: '1000',\n        },\n        'sensor.battery_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'battery_consumption',\n          state: '0',\n        },\n        'sensor.battery_charging': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'battery_charging',\n          state: '1000',\n        },\n        'sensor.battery_charge': {\n          attributes: {\n            unit_of_measurement: '%',\n          },\n          entity_id: 'sensor.battery_charge',\n          state: '99',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  const setBatteryState = async (state: string) => {\n    hass.states['sensor.battery_charge'].state = state;\n    card.setAttribute('hass', JSON.stringify(hass));\n    await elementUpdated(card);\n    await card.setConfig(config);\n  };\n\n  it('has battery_entity, text and icon', async () => {\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'No sum of battery charging flows in acc_text of battery_entity'\n    ).contains('2 kW');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '99 %'\n    );\n  });\n\n  it('has grid to battery charging line and circle', async () => {\n    const gridToBatteryLine = teslaCard?.querySelector(\n      '#grid_to_battery_entity_line'\n    );\n    if (gridToBatteryLine === null || gridToBatteryLine === undefined) {\n      assert.fail('No grid_to_battery_entity_line element found');\n    }\n    const gridToBatteryCircle = teslaCard?.querySelector(\n      '#grid_to_battery_entity_circle'\n    );\n    if (gridToBatteryCircle === null || gridToBatteryCircle === undefined) {\n      assert.fail('No grid_to_battery_entity_circle element found');\n    }\n\n    expect(gridToBatteryLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has solar to battery charging line and circle', async () => {\n    const SolarToBatteryLine = teslaCard?.querySelector(\n      '#generation_to_battery_entity_line'\n    );\n    if (SolarToBatteryLine === null || SolarToBatteryLine === undefined) {\n      assert.fail('No generation_to_battery_entity_line element found');\n    }\n    const SolarToBatteryCircle = teslaCard?.querySelector(\n      '#generation_to_battery_entity_circle'\n    );\n    if (SolarToBatteryCircle === null || SolarToBatteryCircle === undefined) {\n      assert.fail('No generation_to_battery_entity_circle element found');\n    }\n\n    expect(SolarToBatteryLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or appliance icons', async () => {\n    const pvEntity = teslaCard?.querySelector('.pv_consumption_entity');\n    if (pvEntity !== null)\n      assert.fail('No pv_consumption_entity element found');\n\n    const gridEntity = teslaCard?.querySelector('.grid_consumption_entity');\n    if (gridEntity !== null) assert.fail('No battery_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('No appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('No appliance2_consumption_entity element found');\n  });\n\n  it('has battery at 100%', async () => {\n    await setBatteryState('100');\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '100 %'\n    );\n  });\n\n  it('has battery at 83%', async () => {\n    await setBatteryState('83');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-90');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '83 %'\n    );\n  });\n\n  it('has battery at 73%', async () => {\n    await setBatteryState('73');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-80');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '73 %'\n    );\n  });\n\n  it('has battery at 65%', async () => {\n    await setBatteryState('65');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-70');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '65 %'\n    );\n  });\n\n  it('has battery at 15%', async () => {\n    await setBatteryState('15');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-20');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '15 %'\n    );\n  });\n\n  it('has battery at 6%', async () => {\n    await setBatteryState('6');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-10');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '6 %'\n    );\n  });\n\n  it('has battery at 5%', async () => {\n    await setBatteryState('5');\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-charging-outline');\n    expect(batteryEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '5 %'\n    );\n  });\n});\n"
  },
  {
    "path": "test/batteryWithoutExtra.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard battery tests', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      house_entity: 'sensor.house_consumption',\n      // battery_entity: 'sensor.battery_consumption',\n      battery_to_house_entity: 'sensor.battery_consumption',\n    };\n    hass = {\n      states: {\n        'sensor.house_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'House consumption',\n          },\n          entity_id: 'sensor.house_consumption',\n          state: '1300',\n        },\n        'sensor.battery_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'battery_consumption',\n          state: '1300',\n        },\n        'sensor.battery_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'sensor.battery_to_house',\n          state: '1300',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    // let iframe = document.createElement('iframe');\n    // document.body.appendChild(iframe);\n    // let div = document.createElement('div');\n    // document.body.\n    // console.log(\"document width \" + document.body.clientWidth);\n    // document.body.appendChild(card);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has house_entity, text and icon', async () => {\n    const houseEntity = teslaCard?.querySelector('.house_entity');\n\n    if (houseEntity === null || houseEntity === undefined)\n      assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '1.3 kW'\n    );\n    expect(\n      houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:home');\n  });\n\n  it('has battery_entity, text and icon', async () => {\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(batteryEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '1.3 kW'\n    );\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-medium');\n  });\n\n  it('has battery to house consumption line and circle', async () => {\n    // await setCardConsumingFromGrid();\n    const batteryToHouseLine = teslaCard?.querySelector(\n      '#battery_to_house_entity_line'\n    );\n    if (batteryToHouseLine === null || batteryToHouseLine === undefined) {\n      assert.fail('No battery_to_house_line element found');\n    }\n    const batteryToHouseCircle = teslaCard?.querySelector(\n      '#battery_to_house_entity_circle'\n    );\n    if (batteryToHouseCircle === null || batteryToHouseCircle === undefined) {\n      assert.fail('No batter_to_house_entity_circle element found');\n    }\n\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    expect(batteryToHouseLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or appliance icons', async () => {\n    const pvEntity = teslaCard?.querySelector('.generation_entity');\n    if (pvEntity !== null) assert.fail('No generation_entity element found');\n\n    const gridEntity = teslaCard?.querySelector('.grid_consumption_entity');\n    if (gridEntity !== null) assert.fail('No battery_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('No appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('No appliance2_consumption_entity element found');\n  });\n\n  it('has battery icon', async () => {\n    card.requestUpdate();\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity === null || batteryEntity === undefined)\n      assert.fail('No battery_entity element found');\n    expect(\n      batteryEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:battery-medium');\n  });\n});\n"
  },
  {
    "path": "test/colouringOfBubblesDependingOnProduction.test.ts",
    "content": "import { expect, elementUpdated, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('Colouring of bubble depending on production test', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      generation_to_house_entity: 'sensor.generation_to_house',\n      battery_to_house_entity: 'sensor.battery_to_house',\n      grid_to_house_entity: 'sensor.grid_to_house',\n      change_house_bubble_color_with_flow: 1,\n    };\n    hass = {\n      states: {\n        'sensor.generation_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'generation_to_house',\n          state: '1100.1221',\n        },\n        'sensor.battery_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'battery_to_house',\n          state: '2051.1221',\n        },\n        'sensor.grid_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'grid_to_house',\n          state: '1050.1221',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>haCard.querySelector('#tesla-style-solar-power-card');\n    if (teslaCard === null || teslaCard === undefined) assert.fail('No tesla-style-card');\n  });\n\n  it('house_icon shoud have success colour', async () => {\n    const houseEntity = <HTMLElement>teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined) assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''), 'sum of consumptions in mixed house consumption is wrong').contains(\n      '4.2 kW'\n    );\n    expect(houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:home');\n\n    expect(houseEntity.style.color).to.equal('var(--success-color)');\n  });\n\n  const setGenerationToHouseFlow = async (state: string) => {\n    hass.states['sensor.generation_to_house'].state = state;\n    card.setAttribute('hass', JSON.stringify(hass));\n    await elementUpdated(card);\n    await card.setConfig(config);\n  };\n\n  it('house_icon shoud have warning colour', async () => {\n    await setGenerationToHouseFlow('2500');\n    const houseEntity = <HTMLElement>teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined) assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''), 'sum of consumptions in mixed house consumption is wrong').contains(\n      '5.6 kW'\n    );\n    expect(houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:home');\n\n    expect(houseEntity.style.color).to.equal('var(--warning-color)');\n  });\n\n  const setGridToHouseFlow = async (state: string) => {\n    hass.states['sensor.grid_to_house'].state = state;\n    card.setAttribute('hass', JSON.stringify(hass));\n    await elementUpdated(card);\n    await card.setConfig(config);\n  };\n\n  it('house_icon shoud have no colour', async () => {\n    await setGridToHouseFlow('3500');\n    const houseEntity = <HTMLElement>teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined) assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''), 'sum of consumptions in mixed house consumption is wrong').contains(\n      '6.7 kW'\n    );\n    expect(houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:home');\n    expect(houseEntity.style.color).to.equal('var(--info-color)');\n  });\n});\n"
  },
  {
    "path": "test/defaultConfig.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with defaultConfig', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      grid_to_house_entity: 'sensor.grid_to_house',\n      grid_entity: 'sensor.grid_consumption',\n    };\n    hass = {\n      states: {\n        'sensor.grid_to_house': {\n          attributes: {\n            unit_of_measurement: 'w',\n          },\n          entity_id: 'sensor.grid_to_house',\n          state: '500.000000001',\n        },\n        'sensor.grid_consumption': {\n          attributes: {\n            unit_of_measurement: 'w',\n          },\n          entity_id: 'sensor.grid_consumption',\n          state: '500.000000001',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    // let iframe = document.createElement('iframe');\n    // document.body.appendChild(iframe);\n    // let div = document.createElement('div');\n    // document.body.\n    // console.log(\"document width \" + document.body.clientWidth);\n    // document.body.appendChild(card);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has card size 5', () => {\n    expect(card.getCardSize()).to.equal(5);\n  });\n\n  /* it('has a title \"Powerhouse\"', async () => {\n    if(haCard === null || haCard === undefined) assert.fail(\"No ha-card\");\n    if(haCard.shadowRoot === null) assert.fail(haCard.outerHTML);\n    console.log(haCard);\n    expect(haCard.shadowRoot.querySelector('h1')?.innerText).is.equal('Powerhouse');\n  }); */\n\n  it('has no warnings or errors', async () => {\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    console.log(card.innerHTML);\n    expect(card.shadowRoot.querySelectorAll('.message').length).to.equal(0);\n  });\n\n  it('has grid_entity and icon', async () => {\n    const gridEntity = teslaCard?.querySelector('.grid_entity');\n    if (gridEntity === null || gridEntity === undefined)\n      assert.fail('No grid_entity element found');\n    expect(gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '0.5 kW'\n    );\n    expect(\n      gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:transmission-tower');\n  });\n\n  it('has house_entity, text and icon', async () => {\n    const houseEntity = teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined)\n      assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains(\n      '0.5 kW'\n    );\n    expect(\n      houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:home');\n  });\n\n  it('has grid to house consumption line and circle', async () => {\n    // await setCardConsumingFromGrid();\n    const gridConsumptionLine = teslaCard?.querySelector(\n      '#grid_to_house_entity_line'\n    );\n    if (gridConsumptionLine === null || gridConsumptionLine === undefined) {\n      assert.fail('No grid_to_house_entity_line element found');\n    }\n    const gridConsumptionCircle = teslaCard?.querySelector(\n      '#grid_to_house_entity_circle'\n    );\n    if (gridConsumptionCircle === null || gridConsumptionCircle === undefined) {\n      assert.fail('No grid_to_house_entity_circle element found');\n    }\n\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    expect(gridConsumptionLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, battery or appliance icons', async () => {\n    const pvEntity = teslaCard?.querySelector('.generation_entity');\n    if (pvEntity !== null) assert.fail('No generation_entity element found');\n\n    const batteryEntity = teslaCard?.querySelector('.battery_entity');\n    if (batteryEntity !== null) assert.fail('No battery_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('No appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('No appliance2_consumption_entity element found');\n  });\n});\n"
  },
  {
    "path": "test/extraAppliances.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with extra appliances', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      house_entity: 'sensor.house_consumption',\n      grid_to_house_entity: 'sensor.grid_to_house',\n      appliance1_consumption_entity: 'sensor.car_consumption',\n      appliance1_extra_entity: 'sensor.car_soc',\n      appliance2_consumption_entity: 'sensor.heating_consumption',\n      appliance2_extra_entity: 'sensor.heating_current_function',\n    };\n    hass = {\n      states: {\n        'sensor.heating_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'Heating consumption',\n          },\n          entity_id: 'heating_consumption',\n          state: '1000',\n        },\n        'sensor.heating_current_function': {\n          attributes: {\n            unit_of_measurement: null,\n            friendly_name: 'Heating function',\n          },\n          entity_id: 'heating_current_function',\n          state: 'Warm water',\n        },\n        'sensor.car_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'car_consumption',\n          state: '2000',\n        },\n        'sensor.car_soc': {\n          attributes: {\n            unit_of_measurement: '%',\n          },\n          entity_id: 'car_soc',\n          state: '90',\n        },\n        'sensor.house_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'house_consumption',\n          state: '4000',\n        },\n        'sensor.grid_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'grid_to_house',\n          state: '4000',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has house_entity, text and icon', async () => {\n    const gridEntity = teslaCard?.querySelector('.house_entity');\n    if (gridEntity === null || gridEntity === undefined)\n      assert.fail('No house_entity element found');\n    expect(gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains('4 kW');\n    expect(\n      gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:home');\n  });\n\n  it('has appliance1_consumption_entity, text and icon', async () => {\n    const gridEntity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (gridEntity === null || gridEntity === undefined)\n      assert.fail('No appliance1_consumption_entity element found');\n    expect(\n      gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'No sum of appliance1 charging flows in acc_text of grid_entity'\n    ).contains('2 kW');\n    expect(\n      gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:car-sports');\n    expect(\n      gridEntity?.querySelector('.acc_text_extra')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'Appliance 1 extra text is wrong'\n    ).contains('90 %');\n  });\n\n  it('has appliance2_consumption_entity, text and icon', async () => {\n    const gridEntity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (gridEntity === null || gridEntity === undefined)\n      assert.fail('No appliance2_consumption_entity element found');\n    expect(\n      gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'No sum of appliance2 charging flows in acc_text of grid_entity'\n    ).contains('1 kW');\n    expect(\n      gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:air-filter');\n    expect(\n      gridEntity?.querySelector('.acc_text_extra')?.innerHTML,\n      'Appliance 2 extra text is wrong'\n    ).contains('Warm water');\n  });\n\n  it('has appliance1 line and circle', async () => {\n    const appliance = teslaCard?.querySelector(\n      '#appliance1_consumption_entity_line'\n    );\n    if (appliance === null || appliance === undefined) {\n      assert.fail('No appliance1_consumption_entity_line element found');\n    }\n    const applianceCircle = teslaCard?.querySelector(\n      '#appliance1_consumption_entity_circle'\n    );\n    if (applianceCircle === null || applianceCircle === undefined) {\n      assert.fail('No appliance1_consumption_entity_circle element found');\n    }\n    expect(appliance?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has appliance2 line and circle', async () => {\n    const applianceLine = teslaCard?.querySelector(\n      '#appliance2_consumption_entity_line'\n    );\n    if (applianceLine === null || applianceLine === undefined) {\n      assert.fail('No appliance2_consumption_entity_line element found');\n    }\n    const applianceCircle = teslaCard?.querySelector(\n      '#appliance1_consumption_entity_circle'\n    );\n    if (applianceCircle === null || applianceCircle === undefined) {\n      assert.fail('No appliance2_consumption_entity_circle element found');\n    }\n\n    expect(applianceLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or battery icons', async () => {\n    // assert.fail(haCard?.innerHTML);\n    const pvEntity = teslaCard?.querySelector('.generation_yield_entity');\n    if (pvEntity !== null)\n      assert.fail('pv_consumption_entity element found, should not be there');\n\n    const batteryEntity = teslaCard?.querySelector(\n      '.battery_consumption_entity'\n    );\n    if (batteryEntity !== null)\n      assert.fail(\n        'battery_consumption_entity element found, should not be there'\n      );\n\n    const gridEntity = teslaCard?.querySelector('.grid_consumption_entity');\n    if (gridEntity !== null)\n      assert.fail('grid_consumption_entity element found, should not be there');\n  });\n});\n"
  },
  {
    "path": "test/extraAppliancesNotInHouse.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with extra appliances', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      house_entity: 'sensor.house_consumption',\n      grid_to_house_entity: 'sensor.grid_to_house',\n      appliance1_consumption_entity: 'sensor.car_consumption',\n      appliance1_extra_entity: 'sensor.car_soc',\n      appliance2_consumption_entity: 'sensor.heating_consumption',\n      appliance2_extra_entity: 'sensor.heating_current_function',\n      house_without_appliances_values: 'sensor.heating_current_function',\n    };\n    hass = {\n      states: {\n        'sensor.heating_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'Heating consumption',\n          },\n          entity_id: 'heating_consumption',\n          state: '1000',\n        },\n        'sensor.heating_current_function': {\n          attributes: {\n            unit_of_measurement: null,\n            friendly_name: 'Heating function',\n          },\n          entity_id: 'heating_current_function',\n          state: 'Warm water',\n        },\n        'sensor.car_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'car_consumption',\n          state: '2000',\n        },\n        'sensor.car_soc': {\n          attributes: {\n            unit_of_measurement: '%',\n          },\n          entity_id: 'car_soc',\n          state: '90',\n        },\n        'sensor.house_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'house_consumption',\n          state: '4000',\n        },\n        'sensor.grid_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'grid_to_house',\n          state: '4000',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has house_entity, text and icon', async () => {\n    const houseEntity = teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined)\n      assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains('1 kW');\n    expect(\n      houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:home');\n  });\n});\n"
  },
  {
    "path": "test/gridFeed.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with defaultConfig', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      generation_to_grid_entity: 'sensor.generation_to_grid',\n      battery_to_grid_entity: 'sensor.battery_to_grid',\n      grid_entity: 'sensor.grid_consumption',\n    };\n    hass = {\n      states: {\n        'sensor.battery_to_grid': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'Batter to battery',\n          },\n          entity_id: 'sensor.battery_to_grid',\n          state: '1000',\n        },\n        'sensor.grid_consumption': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'grid_consumption',\n          state: '0',\n        },\n        'sensor.generation_to_grid': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'generation_to_grid',\n          state: '1000',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has grid_entity, text and icon', async () => {\n    const gridEntity = teslaCard?.querySelector('.grid_entity');\n    if (gridEntity === null || gridEntity === undefined)\n      assert.fail('No grid_entity element found');\n    expect(\n      gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'No sum of grid charging flows in acc_text of grid_entity'\n    ).contains('2 kW');\n    expect(\n      gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()\n    ).to.equal('mdi:transmission-tower');\n  });\n\n  it('has battery to grid feed in line and circle', async () => {\n    const batteryToGridLine = teslaCard?.querySelector(\n      '#battery_to_grid_entity_line'\n    );\n    if (batteryToGridLine === null || batteryToGridLine === undefined) {\n      assert.fail('No battery_to_grid_entity_line element found');\n    }\n    const batteryToGridCircle = teslaCard?.querySelector(\n      '#battery_to_grid_entity_circle'\n    );\n    if (batteryToGridCircle === null || batteryToGridCircle === undefined) {\n      assert.fail('No battery_to_grid_entity_circle element found');\n    }\n    expect(batteryToGridLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has solar to grid line and circle', async () => {\n    const solarToGridLine = teslaCard?.querySelector(\n      '#generation_to_grid_entity_line'\n    );\n    if (solarToGridLine === null || solarToGridLine === undefined) {\n      assert.fail('No generation_to_battery_entity_line element found');\n    }\n    const solarToGridCircle = teslaCard?.querySelector(\n      '#generation_to_grid_entity_circle'\n    );\n    if (solarToGridCircle === null || solarToGridCircle === undefined) {\n      assert.fail('No generation_to_battery_entity_circle element found');\n    }\n\n    expect(solarToGridLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or appliance icons', async () => {\n    const pvEntity = teslaCard?.querySelector('.pv_consumption_entity');\n    if (pvEntity !== null)\n      assert.fail('No pv_consumption_entity element found');\n\n    const batteryEntity = teslaCard?.querySelector(\n      '.battery_consumption_entity'\n    );\n    if (batteryEntity !== null)\n      assert.fail('No battery_consumption_entity element found');\n\n    const houseEntity = teslaCard?.querySelector('.house_consumption_entity');\n    if (houseEntity !== null)\n      assert.fail('No house_consumption_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('No appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('No appliance2_consumption_entity element found');\n  });\n});\n"
  },
  {
    "path": "test/setters.ts",
    "content": "import {elementUpdated, fixture, html} from \"@open-wc/testing\";\nimport { TeslaStyleSolarPowerCard } from \"../src/TeslaStyleSolarPowerCard.js\";\n\nconst setCard = async (hass:any, config:any) => {\n  const card = await fixture<TeslaStyleSolarPowerCard>(\n    html`\n        <tesla-style-solar-power-card .hass=${hass} .config=${{}}></tesla-style-solar-power-card>\n      `\n  );\n  await card.setConfig(config);\n  // Call firstUpdated() again because fixture already triggered it the first time.\n  await card.firstUpdated();\n  // TODO: Why is this needed for one test case only: 'has debug warning'?\n  await card.setConfig(config);\n\n  return card;\n};\n\nconst setCardView = async (card:any, view:any) => {\n  card.setAttribute('view', view);\n};\n\nconst setCardAllInactive = async (card:any, hass:any, config:any) => {\n  hass.states['sensor.solar_power'].state = \"0\";\n  if (hass.states['sensor.grid_power']) {\n    hass.states['sensor.grid_power'].state = \"0\";\n  }\n  if (hass.states['sensor.grid_power_consumption']) {\n    hass.states['sensor.grid_power_consumption'].state = \"0\";\n  }\n  if (hass.states['sensor.grid_power_production']) {\n    hass.states['sensor.grid_power_production'].state = \"0\";\n  }\n  if (hass.states['sensor.battery_power']) {\n    hass.states['sensor.battery_power'].state = \"0\";\n  }\n  card.setAttribute('hass', JSON.stringify(hass));\n  await elementUpdated(card);\n  await card.setConfig(config);\n};\n\nexport {setCard, setCardView, setCardAllInactive};"
  },
  {
    "path": "test/solarProduction.test.ts",
    "content": "import { expect, assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with solarConfig', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      generation_to_grid_entity: 'sensor.generation_to_grid',\n      generation_to_battery_entity: 'sensor.generation_to_battery',\n      generation_to_house_entity: 'sensor.generation_to_house',\n      generation_entity: 'sensor.generation_entity',\n    };\n    hass = {\n      states: {\n        'sensor.generation_to_battery': {\n          attributes: {\n            unit_of_measurement: 'W',\n            friendly_name: 'Batter to battery',\n          },\n          entity_id: 'sensor.generation_to_battery',\n          state: '3100.211',\n        },\n        'sensor.generation_entity': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'generation_entity',\n          state: '8100',\n        },\n        'sensor.generation_to_grid': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'generation_to_grid',\n          state: '4000.0000011221',\n        },\n        'sensor.generation_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'generation_to_house',\n          state: '1000.1221',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>(\n      haCard.querySelector('#tesla-style-solar-power-card')\n    );\n    if (teslaCard === null || teslaCard === undefined)\n      assert.fail('No tesla-style-card');\n  });\n\n  it('has generation_entity, text and icon', async () => {\n    const solarYieldEntity = teslaCard?.querySelector('.generation_entity');\n    if (solarYieldEntity === null || solarYieldEntity === undefined)\n      assert.fail('No generation_entity element found');\n    expect(\n      solarYieldEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, ''),\n      'No sum of grid charging flows in acc_text of grid_entity'\n    ).contains('8.1 kW');\n    expect(\n      solarYieldEntity\n        ?.querySelector('.acc_icon')\n        ?.getAttribute('icon')\n        ?.toString()\n    ).to.equal('mdi:solar-panel-large');\n  });\n\n  it('has solar to house feed line and circle', async () => {\n    const solarToHouseLine = teslaCard?.querySelector(\n      '#generation_to_house_entity_line'\n    );\n    if (solarToHouseLine === null || solarToHouseLine === undefined) {\n      assert.fail('No generation_to_house_entity_line element found');\n    }\n    const solarToHouseCircle = teslaCard?.querySelector(\n      '#generation_to_house_entity_circle'\n    );\n    if (solarToHouseCircle === null || solarToHouseCircle === undefined) {\n      assert.fail('No generation_to_house_entity_circle element found');\n    }\n    expect(solarToHouseLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has solar to grid line and circle', async () => {\n    const solarToGridLine = teslaCard?.querySelector(\n      '#generation_to_grid_entity_line'\n    );\n    if (solarToGridLine === null || solarToGridLine === undefined) {\n      assert.fail('No generation_to_grid_entity_line element found');\n    }\n    const solarToGridCircle = teslaCard?.querySelector(\n      '#generation_to_grid_entity_circle'\n    );\n    if (solarToGridCircle === null || solarToGridCircle === undefined) {\n      assert.fail('No generation_to_grid_entity_circle element found');\n    }\n    expect(solarToGridLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has solar to battery line and circle', async () => {\n    const solarToBatteryLine = teslaCard?.querySelector(\n      '#generation_to_battery_entity_line'\n    );\n    if (solarToBatteryLine === null || solarToBatteryLine === undefined) {\n      assert.fail('No generation_to_battery_entity_line element found');\n    }\n    const solarToBatteryCircle = teslaCard?.querySelector(\n      '#generation_to_battery_entity_circle'\n    );\n    if (solarToBatteryCircle === null || solarToBatteryCircle === undefined) {\n      assert.fail('No generation_to_battery_entity_circle element found');\n    }\n\n    expect(solarToBatteryLine?.getAttribute('hidden')).to.equal(null);\n  });\n\n  it('has no pv, grid or appliance icons', async () => {\n    const gridEntity = teslaCard?.querySelector('.grid_consumption_entity');\n    if (gridEntity !== null)\n      assert.fail('grid_consumption_entity element found');\n\n    const batteryEntity = teslaCard?.querySelector(\n      '.battery_consumption_entity'\n    );\n    if (batteryEntity !== null)\n      assert.fail('battery_consumption_entity element found');\n\n    const houseEntity = teslaCard?.querySelector('.house_consumption_entity');\n    if (houseEntity !== null)\n      assert.fail('house_consumption_entity element found');\n\n    const appliance1Entity = teslaCard?.querySelector(\n      '.appliance1_consumption_entity'\n    );\n    if (appliance1Entity !== null)\n      assert.fail('appliance1_consumption_entity element found');\n\n    const appliance2Entity = teslaCard?.querySelector(\n      '.appliance2_consumption_entity'\n    );\n    if (appliance2Entity !== null)\n      assert.fail('appliance2_consumption_entity element found');\n  });\n});\n"
  },
  {
    "path": "test/tesla-style-solar-power-card.test.ts",
    "content": "import { html, fixture, expect } from '@open-wc/testing';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\n/*\n describe('TeslaStyleSolarPowerCard', () => {\n  it('has a default title \"Hey there\" and counter 5', async () => {\n    const el = await fixture<TeslaStyleSolarPowerCard>(html`<tesla-style-solar-power-card></tesla-style-solar-power-card>`);\n\n    expect(el.title).to.equal('Hey there');\n    expect(el.counter).to.equal(5);\n  });\n\n  it('increases the counter on button click', async () => {\n    const el = await fixture<TeslaStyleSolarPowerCard>(html`<tesla-style-solar-power-card></tesla-style-solar-power-card>`);\n    el.shadowRoot!.querySelector('button')!.click();\n\n    expect(el.counter).to.equal(6);\n  });\n\n  it('can override the title via attribute', async () => {\n    const el = await fixture<TeslaStyleSolarPowerCard>(html`<tesla-style-solar-power-card title=\"attribute title\"></tesla-style-solar-power-card>`);\n\n    expect(el.title).to.equal('attribute title');\n  });\n  \n\n  it('has an accesible shadowDom', async () => {\n    const el = await fixture<TeslaStyleSolarPowerCard>(html`<tesla-style-solar-power-card></tesla-style-solar-power-card>`);\n\n    await expect(el).shadowDom.to.be.accessible();\n  });\n  \n});\n*/"
  },
  {
    "path": "test/threshold.test.ts",
    "content": "import { expect, /* elementUpdated, */ assert } from '@open-wc/testing';\nimport { LovelaceCardConfig } from 'custom-card-helpers';\nimport { setViewport } from '@web/test-runner-commands';\n\nimport { TeslaStyleSolarPowerCard } from '../src/TeslaStyleSolarPowerCard.js';\nimport '../tesla-style-solar-power-card.js';\nimport { setCard } from './setters.js';\n\ndescribe('TeslaStyleSolarPowerCard with threshold', () => {\n  let card: TeslaStyleSolarPowerCard;\n  let haCard: HTMLElement | null;\n  let teslaCard: HTMLElement | null | undefined;\n  let hass: any;\n  let config: LovelaceCardConfig;\n\n  /** Tests are extended in energy_capable. * */\n\n  beforeEach(async () => {\n    config = {\n      type: 'custom:tesla-style-solar-power-card',\n      name: 'Powerhouse',\n      generation_to_house_entity: 'sensor.generation_to_house',\n      generation_to_grid_entity: 'sensor.generation_to_grid',\n      battery_to_house_entity: 'sensor.battery_to_house',\n      threshold_in_k: 1,\n    };\n    hass = {\n      states: {\n        'sensor.generation_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'sensor.grid_to_house',\n          state: '2801.000000001',\n        },\n        'sensor.generation_to_grid': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'sensor.grid_to_house',\n          state: '1801.000000001',\n        },\n        'sensor.battery_to_house': {\n          attributes: {\n            unit_of_measurement: 'W',\n          },\n          entity_id: 'sensor.battery_to_house',\n          state: '801.000000001',\n        },\n      },\n    };\n    await setViewport({ width: 1200, height: 1000 });\n    card = <TeslaStyleSolarPowerCard>await setCard(hass, config);\n    // let iframe = document.createElement('iframe');\n    // document.body.appendChild(iframe);\n    // let div = document.createElement('div');\n    // document.body.\n    // console.log(\"document width \" + document.body.clientWidth);\n    // document.body.appendChild(card);\n    if (card.shadowRoot === null) assert.fail('No Card Shadowroot');\n    haCard = card.shadowRoot.querySelector('ha-card');\n    if (haCard === null || haCard === undefined) assert.fail('No ha-card');\n    teslaCard = <HTMLElement>haCard.querySelector('#tesla-style-solar-power-card');\n    if (teslaCard === null || teslaCard === undefined) assert.fail('No tesla-style-card');\n    // console.log(teslaCard);\n  });\n\n  /* const setGridToHouseFlow = async (state: string) => {\n    hass.states['sensor.grid_to_house'].state = state;\n    card.setAttribute('hass', JSON.stringify(hass));\n    await elementUpdated(card);\n    await card.setConfig(config);\n  }; */\n\n  it('battery is below threshold', async () => {\n    const gridEntity = teslaCard?.querySelector('.battery_entity');\n    if (gridEntity === null || gridEntity === undefined) assert.fail('No battery_entity element found');\n    expect(gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains('801 W');\n    expect(gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:battery-medium');\n  });\n\n  it('grid is above threshold', async () => {\n    const gridEntity = teslaCard?.querySelector('.grid_entity');\n    if (gridEntity === null || gridEntity === undefined) assert.fail('No grid_entity element found');\n    expect(gridEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains('1.8 kW');\n    expect(gridEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:transmission-tower');\n  });\n\n  it('house is above threshold', async () => {\n    card.requestUpdate();\n    const houseEntity = teslaCard?.querySelector('.house_entity');\n    if (houseEntity === null || houseEntity === undefined) assert.fail('No house_entity element found');\n    expect(houseEntity?.querySelector('.acc_text')?.innerHTML.replace(/<!--[^(-->)]+-->/g, '')).contains('3.6 kW');\n    expect(houseEntity?.querySelector('.acc_icon')?.getAttribute('icon')?.toString()).to.equal('mdi:home');\n  });\n});\n"
  },
  {
    "path": "tsconfig.json",
    "content": "  {\n  \"compilerOptions\": {\n    \"target\": \"es2018\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"noEmitOnError\": true,\n    \"lib\": [\"es2017\", \"dom\"],\n    \"strict\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"experimentalDecorators\": true,\n    \"importHelpers\": true,\n    \"outDir\": \"dist\",\n    \"sourceMap\": true,\n    \"inlineSources\": true,\n    \"rootDir\": \"./\",\n    \"declaration\": true,\n    \"suppressImplicitAnyIndexErrors\": true,\n    \"resolveJsonModule\": true\n  },\n  \"include\": [\"**/*.ts\"]\n}\n"
  },
  {
    "path": "web-dev-server.config.mjs",
    "content": "// import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';\n\n/** Use Hot Module replacement by adding --hmr to the start command */\nconst hmr = process.argv.includes('--hmr');\n\nexport default /** @type {import('@web/dev-server').DevServerConfig} */ ({\n  nodeResolve: true,\n  open: '/demo/',\n  watch: !hmr,\n\n  /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */\n  // esbuildTarget: 'auto'\n\n  /** Set appIndex to enable SPA routing */\n  // appIndex: 'demo/index.html',\n\n  /** Confgure bare import resolve plugin */\n  // nodeResolve: {\n  //   exportConditions: ['browser', 'development']\n  // },\n\n  plugins: [\n    /** Use Hot Module Replacement by uncommenting. Requires @open-wc/dev-server-hmr plugin */\n    // hmr && hmrPlugin({ exclude: ['**/*/node_modules/**/*'], presets: [presets.litElement] }),\n  ],\n\n  // See documentation for all available options\n});\n"
  },
  {
    "path": "web-test-runner.config.mjs",
    "content": "// import { playwrightLauncher } from '@web/test-runner-playwright';\n// import { legacyPlugin } from '@web/dev-server-legacy';\n\nexport default /** @type {import(\"@web/test-runner\").TestRunnerConfig} */ ({\n  files: 'dist/test/**/*.test.js',\n  nodeResolve: true,\n\n  /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */\n  // esbuildTarget: 'auto',\n\n  /** Confgure bare import resolve plugin */\n  // nodeResolve: {\n  //   exportConditions: ['browser', 'development']\n  // },\n\n  /** Amount of browsers to run concurrently */\n  // concurrentBrowsers: 2,\n\n  /** Amount of test files per browser to test concurrently */\n  // concurrency: 1,\n\n  /** Browsers to run tests on */\n  // browsers: [\n  //   playwrightLauncher({ product: 'chromium' }),\n  //   playwrightLauncher({ product: 'firefox' }),\n  //   playwrightLauncher({ product: 'webkit' }),\n  // ],\n\n  // See documentation for all available options\n});\n"
  }
]