slipx06 / sunsynk-power-flow-card

⚡A customizable Home Assistant card to emulate the Sunsynk System flow that's displayed on the Inverter screen.
MIT License
161 stars 48 forks source link

Hooks, Templatable Values, Spacing and Decimals #299

Closed devilliersjohnny closed 3 months ago

devilliersjohnny commented 3 months ago

Is there an existing issue for this?

Current Behavior

Not to sure if the first point goes under feature request or bug, but the rest are feature requests so all are going to be listed here

  1. Prepaid units
    • There are instances where the info cannot be seen because of spacing issues
    • Some utility meters provide two decimal spaces

feature_request

  1. Value other than null
    • Home Assistant doesn't like it if I template a device to equal "null" or null when it doesn't have a value or the sensor is not available/set is there a chance that you could create a different value within your card to equal null (i.e "unavailable") so that that the string value would be replaced with null and there by you would not have to create toggles options to show/hide the entities as they are automatically hidden when they have a null value

feature_request_2

feature_request_3

  1. Hooks or a way to manipulate second level items that aren't entities

    • I am not to sure on how to manipulate the second level options that are not entities have attempted to place a input_number as the option for the solar's mppts and it then defaults to 4 mppts on the card feature_request_4

    How would one go about inputting a value from back end into these?

Expected behaviour

feature_request_1

Possible Solutions

No response

Mode

lite

Context / Reason

My reasoning behind this would be that you would be able to set up a template and therewith modify the cards front end with the like of stack-in-card 'card-mod' 'template-card' which do not have front end editors allowing you to create beautiful dashboards, this would mean that where you would have to dig through the .yaml front code to modify anything you could simply locate the correct template and modify its sensor?

Pho3niX90 commented 3 months ago

For point 2, you are setting the value to a string 'null', you can either try the primitive value of null, or better would be undefined.

For point 3, these values typically doesn't change often, so a sensor isn't required or typically provided by most inverters.HA doesn't have native templating within a card, but there are templating plugins you can install to use. But I suppose this one could follow the same logic as the batteries power (accept value ot sensor)

slipx06 commented 3 months ago

HI It looks like you are rendering your card in Firefox. For some reason it does not respect the layout options. Try using chrome or edge and the card will render as intended.

For point 1 I only display 1 decimal for prepaid units. (${this.toNum(state_prepaid_units.state, 1)}) I don't really see the benefit of showing more than that but at the end of the day its a user preference.

For point 2 the card makes use of a toNum function to handle sensor data. If this is not a number (i.e. a string as you have it defined) it will return 0. I'll look at hiding this when set to 0 for the case of the temperature data.

toNum(val: string | number, decimals: number = -1, invert: boolean = false): number {
        let numberValue = Number(val);
        if (Number.isNaN(numberValue)) {
            return 0;
        }
        if (decimals >= 0) {
            numberValue = parseFloat(numberValue.toFixed(decimals));
        }
        if (invert) {
            numberValue *= -1;
        }
        return numberValue;
    }

For point 3. Some of these option accept sensor data (where it makes sense). Most of them are set through the values provided in the config. The documentation is long but it is mentioned there. If there are other options that would better be set through a sensor let me know and I'll take a look at adding it.

devilliersjohnny commented 3 months ago

Hi thanks for the quick responses...

@Pho3niX90 -

  1. Attempted both, the UI mentions something about null not being defined when used as a primitive, as to the undefined... as I said I am trying to create a template so that I need only edit the sensor entities in the back-end, the front end will have variables/self-created sensors as stand ins
  2. Refer to point two above, I am looking to create a dashboard that runs in HA to Install on my clients systems, that's why I would like to template the options, it would make it easier than working through the code "at the end" every-time. Much like the settings panel that I think you created am looking for a way to configure the card using something like this

feature_request_5

Where you set the values and was hoping for a way to hook into the cards settings?

type: custom:stack-in-card
cards:
  - type: vertical-stack
    cards:
      - type: horizontal-stack
        cards:
          - type: entities
            entities:
              - entity: input_select.edb_display_weather
            card_mod:
              style: |
                ha-card { 
                  --ha-card-background: transparent;
                  border-style: none;
                }
          - type: entities
            entities:
              - entity: input_select.edb_display_load_shedding
            card_mod:
              style: |
                ha-card { 
                  --ha-card-background: transparent;
                  border-style: none;
                }
      - type: custom:stack-in-card
        cards:
          - type: conditional
            conditions:
              - condition: state
                entity: input_select.edb_display_weather
                state: Current
            card:
              type: custom:stack-in-card
              cards:
                - type: horizontal-stack
                  cards:
                    - type: markdown
                      content: >-
                        <center><b><font size='3'>  Weather - <font color =
                        'red'> CURRENT </font> </font></b></center>
                      card_mod:
                        style: |
                          ha-card { 
                            --ha-card-background: transparent;
                            border-style: none;
                          }
                  card_mod:
                    style: |
                      ha-card { 
                        --ha-card-background: transparent;
                        border-style: none;
                      }
                - type: weather-forecast
                  show_current: true
                  show_forecast: false
                  entity: weather.forecast_home
                  forecast_type: daily
                  secondary_info_attribute: wind_speed
                  card_mod:
                    style: |
                      ha-card { 
                        --ha-card-background: transparent;
                        border-style: none;
                        margin: -30px 0px 0px 0px;
                      }
              card_mod:
                style: |
                  ha-card { 
                    --ha-card-background: transparent;
                    border-style: none;
                  }
          - type: conditional
            conditions:
              - condition: state
                entity: input_select.edb_display_weather
                state: Forecast Hourly
            card:
              type: custom:stack-in-card
              cards:
                - type: horizontal-stack
                  cards:
                    - type: markdown
                      content: >-
                        <center><b><font size='3'>  Weather - <font color =
                        'red' > HOURLY </font> Forecast  </font></b></center>
                      card_mod:
                        style: |
                          ha-card { 
                            --ha-card-background: transparent;
                            border-style: none;
                          }
                - type: weather-forecast
                  show_current: false
                  show_forecast: true
                  entity: weather.forecast_home
                  forecast_type: hourly
                  card_mod:
                    style: |
                      ha-card { 
                        --ha-card-background: transparent;
                        border-style: none;
                        margin: -30px 0px 0px 0px;
                      }
              card_mod:
                style: |
                  ha-card { 
                    --ha-card-background: transparent;
                    border-style: none;
                  }
          - type: conditional
            conditions:
              - condition: state
                entity: input_select.edb_display_weather
                state: Forecast Daily
            card:
              type: custom:stack-in-card
              cards:
                - type: horizontal-stack
                  cards:
                    - type: markdown
                      content: >-
                        <center><b><font size='3'>  Weather - <font color
                        ='red'> DAILY </font>Forecast </font></b></center>
                      card_mod:
                        style: |
                          ha-card { 
                            --ha-card-background: transparent;
                            border-style: none;
                          }
                  card_mod:
                    style: |
                      ha-card { 
                        --ha-card-background: transparent;
                        border-style: none;
                      }
                - type: weather-forecast
                  show_current: false
                  show_forecast: true
                  entity: weather.forecast_home
                  forecast_type: daily
                  card_mod:
                    style: |
                      ha-card { 
                        --ha-card-background: transparent;
                        border-style: none;
                        margin: -30px 0px 0px 0px;
                      }
              card_mod:
                style: |
                  ha-card { 
                    --ha-card-background: transparent;
                    border-style: none;
                  }
        card_mod:
          style: |
            ha-card { 
              --ha-card-background: transparent;
              border-style: none;
            }
  - type: conditional
    conditions:
      - condition: state
        entity: input_select.edb_display_load_shedding
        state: Display
    card:
      type: custom:stack-in-card
      cards:
        - type: markdown
          content: |-
            <center><b><font size='4'>
              <font color ='blue'> Load</font> - <font color ='red'>Shedding </font>
            </font></b></center>
          card_mod:
            style: |
              ha-card { 
                --ha-card-background: transparent;
                border-style: none;
              }
        - type: custom:mushroom-chips-card
          chips:
            - type: entity
              entity: sensor.load_shedding_stage_eskom
            - type: template
              content: '{{state_attr(entity, "count")}}/{{state_attr(entity, "limit")}}'
              entity: sensor.load_shedding_sepush_api_quota
              icon: mdi:api
              tap_action:
                action: more-info
          alignment: center
          card_mod:
            style: |
              ha-card { 
                --ha-card-background: transparent;
                border-style: none;
              }
        - type: markdown
          content: >
            {% set stage_sensor = "sensor.load_shedding_stage_eskom" %} {% set
            area_sensor = states("sensor.edb_load_shedding_area_sensor") %} {%
            set area_schedule = state_attr(area_sensor, "forecast") %} {% if
            area_schedule %}
              {% set start_time = area_schedule[0].start_time %}
              {% set end_time = area_schedule[0].end_time %}
              {% if is_state(area_sensor, "off") %}
                {% set starts_in = timedelta(minutes=state_attr(area_sensor, "starts_in")).total_seconds() | int // 60 %}
                {% set mins = starts_in % 60 %}
                {% set hrs = starts_in // 60 % 24 %}
                {% set days = starts_in // 1440 %}
                {% set alert = "Load Shedding starts in {d}d {h}h {m}m ({next})".format(d=days, m=mins, h=hrs, next=as_timestamp(start_time) | timestamp_custom("%H:%M", True)) %}
                {% if starts_in > 1440 %}
                  <ha-alert alert-type="success">{{ states(stage_sensor) }}</ha-alert>
                {% elif 60 < starts_in <= 1440 %}
                  <ha-alert alert-type="warning">{{ alert }}</ha-alert>
                {% else %}
                  <ha-alert alert-type="error">{{ alert }}</ha-alert>
                {% endif %}
              {% else %}
                {% set ends_in = timedelta(minutes=state_attr(area_sensor, "ends_in")).total_seconds() | int // 60 %}
                  {% set mins = ends_in % 60 %}
                  {% set hrs = ends_in // 60 % 24 %}
                  {% set days = ends_in // 1440 %}
                  {% set alert = "Load Shedding ends in {d}d {h}h {m}m ({next})".format(d=days, m=mins, h=hrs, next=as_timestamp(end_time) | timestamp_custom("%H:%M", True)) %}
                  <ha-alert alert-type="error">{{ alert }}</ha-alert>
              {% endif %}
            {% else %}
              {% set stage = state_attr(stage_sensor, "next_stage") %}
              {% set start_time = state_attr(stage_sensor, "next_start_time") %}
              {% set end_time = state_attr(stage_sensor, "next_end_time") %}
              {% set starts_in = timedelta(minutes=state_attr(stage_sensor, "starts_in")).total_seconds() | int // 60 %}
              {% set mins = starts_in % 60 %}
              {% set hrs = starts_in // 60 % 24 %}
              {% set days = starts_in // 1440 %}
              {% if (start_time == 0 or end_time == 0) %}
              {% set alert = "No Load Shedding" %}
              {% else %}
              {% set alert = "Stage {stage} starts in {d}d {h}h {m}m ({next})".format(stage=stage, d=days, m=mins, h=hrs, next=as_timestamp(start_time) | timestamp_custom("%H:%M", True)) %}
              {% endif %}
              <ha-alert alert-type="success">{{ alert }}</ha-alert>
            {% endif %}
          card_mod:
            style: |
              ha-card { 
                --ha-card-background: transparent;
                border-style: none;
              }
        - type: custom:html-template-card
          ignore_line_breaks: true
          content: >
            {% set area_sensor =
            "sensor.load_shedding_area_tshwane_13_lynnwoodglen" %} {% set
            number_of_days = 2 %} {% set show_day_borders = false %} {% set
            show_end_times = true %} {% set timeslots = 48 %} <style>
                @media (prefers-color-scheme: light) {
                    {% if show_day_borders %}
                    .day_container {
                        background-color: #fbeff3 !important;
                    }
                    {% endif %}

                    .current_time_indicator_text,
                    .current_slot_indicator_start_text,
                    .current_slot_indicator_end_text {
                        color: #785551 !important;
                    }

                    .current_time_indicator,
                    .current_slot_indicator_start,
                    .current_slot_indicator_end {
                        background-color: #785551 !important;
                    }

                    .slot {
                        background-color: #f5ddd9 !important;
                    }
                }

                .day_container {
                    {% if show_day_borders %}
                    background-color: #2b2120;
                    border-radius: 0.75rem;
                    {% endif %}
                    padding-top: 0.5rem;
                    padding-bottom: 1.75rem;
                    margin: 0.25rem 0;
                }

                h3.day_heading,
                .current_time_indicator_text,
                .current_slot_indicator_start_text,
                .current_slot_indicator_end_text {
                    font-family: Roboto, Ubuntu, sans-serif;
                    font-weight: 600;
                }

                h3.day_heading {
                    font-size: 1.0rem;
                    margin: 0 0 0 1rem;
                }

                .slot_container {
                    display: grid;
                    grid-template-columns: repeat({{ timeslots }}, 1fr);
                    gap: 0.0625rem;
                    width: calc(100% - 2rem);
                    margin: 0 1rem;
                    line-height: 0.9375rem;
                    position: relative;
                }

                .slot_container .slot:first-of-type {
                    border-top-left-radius: 50%;
                    border-bottom-left-radius: 50%;
                }

                .slot_container .slot:last-of-type {
                    border-top-right-radius: 50%;
                    border-bottom-right-radius: 50%;
                }

                .slot {
                    border-radius: 15%;
                    background-color: #534341;
                }

                .active_slot_stage_1 {
                    background-color: #f6a829 !important;
                }

                .active_slot_stage_2 {
                    background-color: #f8980d !important;
                }

                .active_slot_stage_3 {
                    background-color: #e66e0e !important;
                }

                .active_slot_stage_4 {
                    background-color: #e3493f !important;
                }

                .active_slot_stage_5 {
                    background-color: #d93e3d !important;
                }

                .active_slot_stage_6 {
                    background-color: #cf3131 !important;
                }

                .active_slot_stage_7 {
                    background-color: #b21e1d !important;
                }

                .active_slot_stage_8 {
                    background-color: black !important;
                }

                div.active_slot {
                    background-color: black;
                }

                div.fade_slot {
                    opacity:0.2;
                }

                .current_time_indicator {
                    width: 0.125rem;
                    position: absolute;
                    height: 120%;
                    top: -10%;
                    border-radius: 15%;
                    transform: translate(-50%, 0);
                    background-color: #e6bdb7;
                }

                .current_time_indicator_text {
                    position: absolute;
                    bottom: 140%;
                    transform: translate(-50%, 0);
                    color: #e6bdb7;
                }

                .current_slot_indicator_start {
                    width: 0.125rem;
                    position: absolute;
                    height: 40%;
                    top: 100%;
                    border-radius: 15%;
                    transform: translate(-50%, 0);
                    background-color: #e6bdb7;
                }

                .current_slot_indicator_start_text {
                    position: absolute;
                    top: 150%;
                    transform: translate(-50%, 0);
                    color: #e6bdb7;
                }

                .current_slot_indicator_end {
                    width: 0.125rem;
                    position: absolute;
                    height: 40%;
                    bottom: 100%;
                    border-radius: 15%;
                    transform: translate(-50%, 0);
                    background-color: #e6bdb7;
                }

                .current_slot_indicator_end_text {
                    position: absolute;
                    bottom: 150%;
                    transform: translate(-50%, 0);
                    color: #e6bdb7;
                }
            </style> {% set area_schedule = state_attr(area_sensor, "forecast")
            %} {% if area_schedule is none %}{% set area_schedule = [] %}{%
            endif %} {% for day_offset_idx in range(number_of_days) %}
                {% set today_datetime_midnight = now().replace(hour=0,minute=0,second=0,microsecond=0) + timedelta(days=day_offset_idx) %}
                <div class="day_container">
                    <h3 class="day_heading"
                        style="{% if day_offset_idx == 0 or show_end_times %} margin-bottom: 1.5rem;
                            {% else %} margin-bottom: 0.5rem;
                            {% endif %}">{{ today_datetime_midnight.strftime("%A, %B %-d") }}</h3>
                    <div class="slot_container">
                        {% set ns = namespace(active_class_name="", last_slot_was_active=false, current_slot_was_activated=false) %}
                        {% for half_hour_time_slot_idx in range(timeslots) %}
                            {% set half_hour_time_slot = today_datetime_midnight + timedelta(minutes=30*half_hour_time_slot_idx) %}
                            {% set ns.active_class_name = "" %}
                            {% set ns.current_slot_was_activated = false %}
                            {% for loadshedding in area_schedule %}
                                {% if not ns.current_slot_was_activated %}
                                    {% if loadshedding["start_time"] <= half_hour_time_slot < loadshedding["end_time"] %}
                                        {% if not ns.last_slot_was_active %}
                                            {% set percentage_of_region = (half_hour_time_slot_idx/timeslots)*100 %}
                                            <span class="current_slot_indicator_start" style="left:{{ percentage_of_region }}%">&nbsp;</span>
                                            <span class="current_slot_indicator_start_text" style="left:{{ percentage_of_region }}%;
                                                        {% if half_hour_time_slot.hour == 0 %}transform: none;{% elif half_hour_time_slot.hour == 23 %}transform: translate(-100%,0);{% endif %}">{{ half_hour_time_slot.strftime("%H:%M") }}</span>
                                        {% endif %}
                                        {% set ns.current_slot_was_activated = true %}
                                        {% set ns.last_slot_was_active = true %}
                                        {% set ns.active_class_name = "active_slot active_slot_" + loadshedding['stage']|lower|replace(' ','_') %}
                                    {% endif %}
                                {% endif %}
                            {% endfor %}
                            {% if not ns.current_slot_was_activated %}
                                {% if show_end_times and ns.last_slot_was_active %}
                                    {% set percentage_of_region = (half_hour_time_slot_idx/timeslots)*100 %}
                                    <span class="current_slot_indicator_end"
                                        style="left:{{ percentage_of_region }}%">&nbsp;</span>
                                    <span class="current_slot_indicator_end_text"
                                        style="left:{{ percentage_of_region }}%;
                                                {% if half_hour_time_slot.hour == 0 %}transform: none;{% elif half_hour_time_slot.hour == 23 %}transform: translate(-100%,0);{% endif %}">{{ half_hour_time_slot.strftime("%H:%M") }}</span>
                                {% endif %}
                                {% set ns.last_slot_was_active = false %}
                            {% endif %}
                            <div class="slot {% if now() > half_hour_time_slot + timedelta(minutes=30) %}fade_slot{% endif %} {{ ns.active_class_name }}">&nbsp;</div>
                        {% endfor %}
                        {% if day_offset_idx == 0 %}
                            {% set current_time_indicator_progress = now().hour*2 + now().minute/30 %}
                            {% set percentage_of_region = (current_time_indicator_progress/timeslots)*100 %}
                            <span class="current_time_indicator"
                                style="left:{{ percentage_of_region }}%">&nbsp;</span>
                            {% if not show_end_times %}
                              <span class="current_time_indicator_text"
                                  style="left:{{ percentage_of_region }}%">Now</span>
                            {% endif %}
                        {% endif %}
                    </div>
                </div>
            {% endfor %}
          card_mod:
            style: |
              ha-card
                {background: transparent;
                border-style: none;
              }
        - type: custom:atomic-calendar-revive
          enableModeChange: true
          firstDayOfWeek: 1
          refreshInterval: 1800
          entities:
            - calendar.load_shedding_forecast
          showCurrentEventLine: false
          showMonth: true
          showWeekDay: true
          disableEventLink: true
          showNoEventsForToday: true
          disableLocationLink: true
          showFullDayProgress: false
          showEventIcon: false
          showHiddenText: false
          showCalendarName: false
          calShowDescription: false
          showLastCalendarWeek: true
          disableCalEventLink: true
          disableCalLocationLink: true
          disableCalLink: true
          showDescription: false
          dateFormat: LL
          showDate: false
          sortByStartTime: false
          showRelativeTime: true
          showProgressBar: true
          showLocation: true
          showDeclined: true
          showMultiDayEventParts: false
          showMultiDay: false
          showLoader: false
          maxDaysToShow: 3
          card_mod:
            style: |
              ha-card {
                background: transparent;
                border-style: none;
              }
    card_mod:
      style: |
        ha-card {
          background: transparent;
          border-style: none;
        }
  - type: markdown
    content: >-
      <center><b><font size='6'> <font color = 'blue' > POWER </font><font color
      = 'grey' >flow</font><font color = 'lime' > OVERVIEW
      </font></font></b></center>
    card_mod:
      style: |
        ha-card { 
           --ha-card-background: transparent;
           border-style: none;
        }
  - type: custom:sunsynk-power-flow-card
    cardstyle: lite
    title_colour: blue
    title: null
    show_solar: true
    battery:
      shutdown_soc: 20
      show_daily: true
      auto_scale: false
      show_remaining_energy: true
      invert_power: true
      show_absolute: true
      hide_soc: true
      animation_speed: 8
      max_power: 12000
      energy: 12800
    solar:
      show_daily: true
      mppts: 2
      auto_scale: false
      dynamic_colour: true
      display_mode: 1
      animation_speed: 8
      max_power: 10000
    load:
      show_daily: true
      auto_scale: false
      dynamic_colour: true
      dynamic_icon: true
      show_daily_aux: true
      essential_name: Essential
      additional_loads: 0
      load1_name: a
      load1_icon: mdi:account
      load2_name: b
      load2_icon: mdi:account
      load3_name: c
      load3_icon: mdi:account
      load4_name: d
      load4_icon: mdi:account
      animation_speed: 8
      max_power: 10000
      show_aux: true
      aux_name: aux
      aux_type: mdi:account
      invert_aux: true
      show_absolute_aux: true
      aux_loads: 2
      aux_load1_name: a
      aux_load1_icon: mdi:account
      aux_load2_name: b
      aux_load2_icon: mdi:account
    grid:
      show_daily_buy: true
      show_daily_sell: true
      show_nonessential: true
      auto_scale: false
      invert_grid: false
      energy_cost_decimals: 2
      additional_loads: 3
      nonessential_name: Non Essential
      nonessential_icon: mdi:account
      load1_name: '1'
      load1_icon: mdi:account
      load2_name: '2'
      load2_icon: mdi:account
      load3_name: '3'
      load3_icon: mdi:account
      animation_speed: 8
      max_power: 8000
    show_battery: true
    show_grid: true
    inverter:
      three_phase: false
      autarky: power
      auto_scale: false
      modern: true
    large_font: false
    panel_mode: true
    entities:
      day_pv_energy_108: sensor.edb_pv_total_energy_daily_output
      pv1_power_186: sensor.edb_pv1_power
      pv1_voltage_109: sensor.edb_pv1_voltage
      pv1_current_110: sensor.edb_pv1_current
      pv2_power_187: sensor.edb_pv2_power
      pv2_voltage_111: sensor.edb_pv2_voltage
      pv2_current_112: sensor.edb_pv2_current
      pv3_power_188: sensor.edb_pv3_power
      pv3_current_114: sensor.edb_pv3_current
      pv3_voltage_113: sensor.edb_pv3_voltage
      pv4_power_189: sensor.edb_pv4_power
      pv4_current_116: sensor.edb_pv4_current
      pv4_voltage_115: sensor.edb_pv4_voltage
      solar_sell_247: null
      remaining_solar: sensor.edb_remaining_solar
      environment_temp: null
      battery_power_190: sensor.edb_battery_power
      battery_current_191: sensor.edb_battery_current
      battery_voltage_183: sensor.edb_battery_voltage
      battery_temp_182: sensor.edb_battery_temperature
      battery_soc_184: sensor.edb_battery_soc
      inverter_voltage_154: sensor.edb_inverter_voltage
      inverter_voltage_L2: sensor.edb_inverter_voltage_l2
      inverter_voltage_L3: sensor.edb_inverter_voltage_l3
      load_frequency_192: sensor.edb_inverter_load_frequency
      inverter_current_164: sensor.edb_inverter_current
      inverter_current_L2: sensor.edb_inverter_current_l2
      inverter_current_L3: sensor.edb_inverter_current_l3
      grid_power_169: sensor.edb_inverter_grid_power
      dc_transformer_temp_90: sensor.edb_inverter_dc_transformer_temperature
      radiator_temp_91: sensor.edb_inverter_radiator_temperature
      day_load_energy_84: sensor.edb_load_day_load_energy
      day_aux_energy: sensor.edb_load_aux_day_energy
      essential_power: sensor.edb_load_essential_power_total
      essential_load1: sensor.edb_load_essential_1
      essential_load2: sensor.edb_load_essential_2
      essential_load3: sensor.edb_load_essential_3
      essential_load4: sensor.edb_load_essential_4
      essential_load1_extra: sensor.edb_load_essential_1_extra
      essential_load2_extra: sensor.edb_load_essential_2_extra
      load_power_L1: sensor.edb_load_power_l1
      load_power_L2: sensor.edb_load_power_l2
      load_power_L3: sensor.edb_load_power_l3
      aux_power_166: sensor.edb_load_aux_power
      aux_load1: sensor.edb_load_aux_load_1
      aux_load2: sensor.edb_load_aux_load_2
      aux_load1_extra: sensor.edb_load_load_1_extra
      aux_load2_extra: sensor.edb_load_aux_load_2_extra
      aux_connected_status: null
      day_grid_import_76: sensor.edb_grid_day_import_energy
      day_grid_export_77: sensor.edb_grid_day_export_energy
      grid_ct_power_172: sensor.edb_grid_ct_power_l1
      grid_ct_power_L2: sensor.edb_grid_ct_power_l2
      grid_ct_power_L3: sensor.edb_grid_ct_power_l3
      grid_ct_power_total: sensor.edb_grid_ct_power_total
      grid_voltage: sensor.edb_grid_voltage
      nonessential_power: null
      nonessential_load1: null
      nonessential_load2: null
      nonessential_load3: null
      grid_connected_status_194: sensor.edb_grid_connected_status
      energy_cost_buy: null
      energy_cost_sell: null
      prepaid_units: null
      priority_load_243: sensor.edb_inverter_priority_load
      inverter_power_175: sensor.edb_inverter_power
      pv_total: sensor.edb_pv_total_power_output
      day_battery_charge_70: sensor.edb_battery_energy_in_daily
      day_battery_discharge_71: sensor.edb_battery_energy_out_daily
    card_mod:
      style: |
        ha-card { 
          --ha-card-background: transparent;
          border-style: none;
        }
card_mod:
  style: |
    ha-card { 
      --ha-card-background: transparent;
      border-style: none;
    }
devilliersjohnny commented 3 months ago

@slipx06 - Thanks for getting back to me

  1. The decimals are a nice to have... I have created a entity on the settings ( see below ) that gives this data and allows for the manipulation thereof... but am looking to include this in my overview page of my EDB Dashboard so that a user could see this at a glance?!

feature_request_5

  1. If I understand the function correctly it's saying that a string will be tested to see if its a number, if it is it will be cast as a float? Or are you saying that I should ensure that it is a number by pre-casting it? If so how do I do that?

  2. Please refer to the post just above with the code... HA does not allow UI config in this case... So I was hoping that I could plug into the card at certain points with my templates?

Thank you both for your responses @slipx06 @Pho3niX90

Pho3niX90 commented 3 months ago

@slipx06 I am wondering if we shouldn't use the state object to determine if an entity should be used, rather than a set value? Would need to identify which sensors would be applicable for this, and perhaps, even use a conditional such as ((state == None || available == None) && timeSince > 1 minute).

@devilliersjohnny Come to think of it, can you set your templates states to None?

Roving-Ronin commented 3 months ago

Re the battery temperature and not having a numerical value to the sensor, can't you just use 'float(0)' so it defaults to 0 if no reading in available?

I do this for the derived sensors for Huawei inverters configurations, that also works for multiple inverters/batteries (setup for 2 in example configs). Here's a card with the example config applied and NO battery attached. Battery sensors show zero and no issues/errors:

image

devilliersjohnny commented 3 months ago

@slipx06 like your thinking!

Tested it out...

states_03

It however does not yield the result of none in the "Developer Tools >> STATES" Tab, tested it with a few different strings and setting it to none seems to be the only one that returns a different value 'unknown'

states_02

Could you do anything with that?

As far as I can determine it is probably because of the way the template is set up... ie. the template includes "unit_of_measurement" and "device_class" probably forcing it into the field of being a number, but 'None' seems to be a special type within the code?

states_01

devilliersjohnny commented 3 months ago

Apologies my head is all over the place today!

@Roving-Ronin the idea behind what I wanted to achieve is that the sensor is hidden when it does not have a value so setting the value to 0 would result in there being a reading when there should be?! It is a valid hypothesis but the idea @Pho3niX90 came up with would allow for the hiding of the entity when misconfigured? That is if he could use what I told him above!

Pho3niX90 commented 3 months ago

Apologies my head is all over the place today!

@Roving-Ronin the idea behind what I wanted to achieve is that the sensor is hidden when it does not have a value so setting the value to 0 would result in there being a reading when there should be?! It is a valid hypothesis but the idea @Pho3niX90 came up with would allow for the hiding of the entity when misconfigured? That is if he could use what I told him above!

That should be fine. the unknown would actually work perfectly. I believe you can set it to a primitive unknown as well.

Which items would you like to be able to hide? Only battery or others?

Edit. Come to think of it, if you handle this with your frontend config, are you not able to seitch the show battery option to false when the value isn't a number?

devilliersjohnny commented 3 months ago

@Pho3niX90 addressing the edit first, the option that I cam looking to hide is the temperature not the entire battery... and there is no option to switch for that! But along those lines it would probably force you to create a whole lot of additional switches/entities to handle all the new options and the card setup page would become over crowded.

Setting 'Unknown' to the primitive... as I mentioned above 'None' is the only one that sets the state to 'Unknown' where as 'Unknown' sets to to 'Unavailable' which is not unique as almost all other strings do the same

Would it be possible to code for all entities that read 'Unknown' to be hidden? ( I know its a big ask )

Pho3niX90 commented 3 months ago

so we could in theory add a hidden config item, named dynamicConfig for example, to enable hiding items when the value is invalid (unknown). There are use cases when a sensor could realistically return a state of None or unknown, (during startup, battery shutdown/sleep), so not sure if this should be default behavior.

@slipx06 what do you think?

slipx06 commented 3 months ago

@Pho3niX90 makes sense but this should not be default behavior. How many other examples besides battery temp are there? Seems like quite a bit of effort with little benefit and would require updating all the CSS display properties?

devilliersjohnny commented 3 months ago

Hi folks apologies have been AFK for the past few days... So @slipx06 to answer the question above, all of the temperature sensors follow the same pattern ( Battery, Transformer, Radiator and Environmental ). Think about it, the daily grid buy and sell should also follow this pattern??? But then this would open a larger can of worms as to most if not all of your energy totals ( Daily Solar, load, battery charges ans grid buy/sell ) so it might reach further than just the battery temp?

Edit

You might then even be able to automatically detect solar, batteries, grid, aux loads and then all the rest as well?! I apologize in advance if I made a lot of work for you!

slipx06 commented 3 months ago

I often want to see power and energy values that are 0. I will apply this to the temp sensors as a start and we can then proceed on a case by case basis.

devilliersjohnny commented 3 months ago

Its not the ability to set them to 0 that should disable them... its the states of thereof that should point to the disabling of the device! Remember that temperatures can be 0 and even go into the negatives so the numerical value should not be the deciding factor

slipx06 commented 3 months ago

@Pho3niX90 we could update the convertToCustomEntity function

isValid: () => entity?.state !== null && entity.state !== undefined && entity.state !== 'unknown' || false,

and then for each of the entities we want to check

//Check for unknown states
let isValidStateBatteryTemp = stateBatteryTemp.isValid

and finally update the CSS display property

display="${!config.show_battery || !data.isValidStateBatteryTemp ? 'none' : ''}"

Pho3niX90 commented 3 months ago

@Pho3niX90 we could update the convertToCustomEntity function

isValid: () => entity?.state !== null && entity.state !== undefined && entity.state !== 'unknown' || false,

and then for each of the entities we want to check

//Check for unknown states
let isValidStateBatteryTemp = stateBatteryTemp.isValid

and finally update the CSS display property

display="${!config.show_battery || !data.isValidStateBatteryTemp ? 'none' : ''}"

That sounds perfect.

Will quickly have a look today. I want to introduce some unit tests as well so we can easily test new changes.

Pho3niX90 commented 3 months ago

So changes have been done to the compact card, if you could test it out. https://github.com/slipx06/sunsynk-power-flow-card/pull/309/files#diff-6332f9abe6bcdb8a677cd02890c286fe03ad412b0109c49a6f5fcfc5c3d00c45

slipx06 commented 3 months ago

@devilliersjohnny please test https://github.com/slipx06/sunsynk-power-flow-card/releases/tag/v4.24.1

devilliersjohnny commented 3 months ago

@slipx06 @Pho3niX90 Thank you! Is working beautifully! Even those that aren't temperature based are disappearing with the 'None' option! What would you guys think about the card auto configuring it self? I mean that if there are entities present it will auto fill the options, for example lets say that you only have two mppts configured, the card then automatically sets the mppt count to two?

Pho3niX90 commented 3 months ago

@slipx06 @Pho3niX90 Thank you! Is working beautifully! Even those that aren't temperature based are disappearing with the 'None' option! What would you guys think about the card auto configuring it self? I mean that if there are entities present it will auto fill the options, for example lets say that you only have two mppts configured, the card then automatically sets the mppt count to two?

I syppose we have enough sensors per brand to attempt something like this, wince they are split into their own classes as well now.

At first setup, and upon choosing the desired brand, we can do a quick check on 3 sensors if it returns data. If so, use that template.

devilliersjohnny commented 3 months ago

@Pho3niX90 I'm not quite following?! Apologies but I haven't looked art the source code yet, therefore I cannot say that I am familiar with the workings of the card...