E3DC Solar Power Station Widget

Out of the box :slight_smile:


Thanks a lot

You mentioned above also the issue that the PV power label is shifted down towards the center when the battery is neither charging nor discharging. I think that’s a rare situation that doesn’t happen in most systems, which is probably why it has not been widely noticed. But it is possible. So I added some transparent dummy text that is placed instead of the battery arrows and power value in that case. It should keep the PV power roughly where it should be. I have tested with light and dark modes and it seems to work as far as I can tell, although the widget in general does not look so great in dark mode.

So here is version 1.4:

uid: E3DC_Widget
tags:
  - e3dc
  - pv
  - version 1.4
props:
  parameters:
    - context: item
      label: Stromnetz Bezug / Grid-to-Home
      name: gridtohome
      required: true
      type: TEXT
    - context: item
      label: Stromnetz Einspeisung / Home-to-Grid
      name: hometogrid
      required: true
      type: TEXT
    - context: item
      label: Haushalt Verbrauch / Household Consumption
      name: household_consumption
      required: true
      type: TEXT
    - context: item
      label: PV Leistung / PV Power
      name: pv_power
      required: true
      type: TEXT
    - context: item
      label: Batterie Laden / Battery Charge
      name: battery_charge
      required: true
      type: TEXT
    - context: item
      label: Batterie Entladen / Battery Discharge
      name: battery_discharge
      required: true
      type: TEXT
    - context: item
      label: Batterie Ladezustand / Battery SoC
      name: batterylevel
      required: true
      type: TEXT
    - context: item
      label: Wallbox Ladestrom / Wallbox Charging
      name: wallbox_charge
      required: false
      type: TEXT
    - context: item
      label: externer Stromproduzent / External Inverter
      name: external_energy
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Feb 28, 2024, 3:49:14 PM
component: f7-card
config:
  style:
    padding: 1%
    justify-content: center
  class:
    - display-flex
    - align-items-center
  stylesheet: >
    .arrowleft {
       #position: absolute;
       #top: 50%;
       #left: 50%;
       #transform: translate(50%,50%);
     }
    .arrowleft div {
       display: block;
       width: 0.4em;
       height: 0.4em;
       border-bottom: 0.15em solid var(--f7-text-color);
       border-right: 0.15em solid var(--f7-text-color);
       transform: rotate(135deg);
       #margin: -25px;
       animation: animate 2s infinite;
    }  .arrowleft div:nth-child(2){
        animation-delay: -0.2s;
    }  .arrowleft div:nth-child(3){
        animation-delay: -0.4s;
    }  @keyframes animate {
        0%{
            opacity: 0;
            transform: rotate(135deg) translate(-0.7em,-0.7em);
        }
        50%{
            opacity: 1;
        }
        100%{
            opacity: 0;
            transform: rotate(135deg) translate(0.7em,0.7em);
        }
    }                 .arrowright {
       #position: absolute;
       #top: 50%;
       #left: 50%;
       #transform: translate(-50%,-50%);
     }
    .arrowright div {
       display: block;
       width: 0.4em;
       height: 0.4em;
       border-bottom: 0.15em solid var(--f7-text-color);
       border-right: 0.15em solid var(--f7-text-color);
       transform: rotate(-45deg);
       #margin: -25px;
       animation: animate2 2s infinite;
    }    .arrowright div:nth-child(2) {
        animation-delay: 0.2s;
    }      .arrowright div:nth-child(3) {
        animation-delay: 0.4s;
    }     @keyframes animate2 {
        0%{
            opacity: 0;
            transform: rotate(-45deg) translate(-0.7em,-0.7em);
        }
        50%{
            opacity: 1;
        }
        100%{
            opacity: 0;
            transform: rotate(-45deg) translate(0.7em,0.7em);
        }
    }                   
slots:
  default:
    - component: f7-col
      config:
        style:
          align-items: center
          display: flex
          flex-direction: column
          justify-content: center
          width: 20%
      slots:
        default:
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_pv_sun
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_extern
              style:
                margin-top: 1em
              visible: '=props.external_energy ? (Number.parseFloat(items[props.external_energy].state.split(" ")[0]) > 0) ? true : false : false'
          - component: Label
            config:
              style:
                font-size: 1em
                font-weight: bold
                margin-top: 0.7em
                text-align: center
              text: =items[props.batterylevel].state
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_0
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 2) ? true : false'
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_25
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 2 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 25) ? true : false'
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_50
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 25 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 50) ? true : false'
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_75
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 50 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 75) ? true : false'
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_90
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 75 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 99) ? true : false'
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_akku_100
              visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 99) ? true : false'
    - component: f7-col
      config:
        style:
          align-items: center
          display: flex
          flex-direction: column
          width: 20%
      slots:
        default:
          - component: f7-row
            config:
              class: arrowright
              visible: '=(Number.parseFloat(items[props.pv_power].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: f7-row
            slots:
              default:
                - component: Label
                  config:
                    style:
                      font-size: 1em
                      font-weight: bold
                      padding-bottom: 2.1em
                      text-align: center
                    text: =Math.abs(Number.parseFloat(items[props.pv_power].state.split(" ")[0])) + ' ᵂ'
          - component: f7-row
            config:
              class: arrowright
              visible: '=props.external_energy ? (Number.parseFloat(items[props.external_energy].state.split(" ")[0]) > 0) ? true : false : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: f7-row
            slots:
              default:
                - component: Label
                  config:
                    style:
                      font-size: 1em
                      font-weight: bold
                      padding-bottom: 2.1em
                      text-align: center
                    text: '=props.external_energy ? Math.abs(Number.parseFloat(items[props.external_energy].state.split(" ")[0])) + " ᵂ" : ""'
                    visible: '=props.external_energy ? (Number.parseFloat(items[props.external_energy].state.split(" ")[0]) > 0) ? true : false : false'
          - component: f7-row
            config:
              class: arrowright
              visible: '=(Number.parseFloat(items[props.battery_discharge].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: f7-row
            config:
              class: arrowleft
              visible: '=(Number.parseFloat(items[props.battery_charge].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowleft div
                - component: Label
                  config:
                    class: arrowleft div
                - component: Label
                  config:
                    class: arrowleft div
          - component: f7-row
            config:
              visible: '=(Number.parseFloat(items[props.battery_charge].state.split(" ")[0]) == 0) && (Number.parseFloat(items[props.battery_discharge].state.split(" ")[0]) == 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    style:
                      font-size: 0.6em
                      color: transparent
                      text-align: center
                    text: ='0'
          - component: Label
            config:
              style:
                color: green
                font-size: 1em
                font-weight: bold
                text-align: center
              text: =Math.abs(Number.parseFloat(items[props.battery_charge].state.split(" ")[0])) + ' ᵂ'
              visible: '=(Number.parseFloat(items[props.battery_charge].state.split(" ")[0]) > 0) ? true : false'
          - component: Label
            config:
              style:
                color: red
                font-size: 1em
                font-weight: bold
                text-align: center
              text: =Math.abs(Number.parseFloat(items[props.battery_discharge].state.split(" ")[0])) + ' ᵂ'
              visible: '=(Number.parseFloat(items[props.battery_discharge].state.split(" ")[0]) > 0) ? true : false'
          - component: Label
            config:
              style:
                font-size: 1em
                color: transparent
                text-align: center
              text: ='0'
              visible: '=(Number.parseFloat(items[props.battery_charge].state.split(" ")[0]) == 0) && (Number.parseFloat(items[props.battery_discharge].state.split(" ")[0]) == 0) ? true : false'
    - component: f7-col
      config:
        style:
          align-items: center
          display: flex
          flex-direction: column
          justify-content: center
          width: 20%
      slots:
        default:
          - component: f7-block
            config:
              style:
                align-items: center
                display: flex
                flex-direction: column
                justify-content: center
            slots: {}
          - component: oh-icon
            config:
              height: 60em
              icon: e3dc
              width: 60em
    - component: f7-col
      config:
        style:
          align-items: center
          display: flex
          flex-direction: column
          justify-content: center
          width: 20%
      slots:
        default:
          - component: f7-row
            config:
              class: arrowright
              visible: '=(Number.parseFloat(items[props.hometogrid].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: f7-row
            config:
              class: arrowleft
              visible: '=(Number.parseFloat(items[props.gridtohome].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowleft div
                - component: Label
                  config:
                    class: arrowleft div
                - component: Label
                  config:
                    class: arrowleft div
          - component: Label
            config:
              style:
                color: red
                font-size: 1em
                font-weight: bold
                padding-bottom: 2.1em
                text-align: center
              text: =Math.abs(Number.parseFloat(items[props.gridtohome].state.split(" ")[0])) + ' ᵂ'
              visible: '=(Number.parseFloat(items[props.hometogrid].state.split(" ")[0]) == 0) ? true : false'
          - component: Label
            config:
              style:
                color: green
                font-size: 1em
                font-weight: bold
                padding-bottom: 2.1em
                text-align: center
              text: =Math.abs(Number.parseFloat(items[props.hometogrid].state.split(" ")[0])) + ' ᵂ'
              visible: '=(Number.parseFloat(items[props.hometogrid].state.split(" ")[0]) > 0) ? true : false'
          - component: f7-row
            config:
              class: arrowright
              visible: '=props.wallbox_charge ? (Number.parseFloat(items[props.wallbox_charge].state.split(" ")[0]) > 0) ? true : false : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: Label
            config:
              style:
                font-size: 1em
                font-weight: bold
                padding-bottom: 2.1em
                text-align: center
              text: '=props.wallbox_charge ? Math.abs(Number.parseFloat(items[props.wallbox_charge].state.split(" ")[0])) + " ᵂ" : ""'
              visible: '=props.wallbox_charge ? (Number.parseFloat(items[props.wallbox_charge].state.split(" ")[0]) > 0) ? true : false : false'
          - component: f7-row
            config:
              class: arrowright
              visible: '=(Number.parseFloat(items[props.household_consumption].state.split(" ")[0]) > 0) ? true : false'
            slots:
              default:
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
                - component: Label
                  config:
                    class: arrowright div
          - component: Label
            config:
              style:
                font-size: 1em
                font-weight: bold
                text-align: center
              text: =Math.abs(Number.parseFloat(items[props.household_consumption].state.split(" ")[0])) + ' ᵂ'
    - component: f7-col
      config:
        style:
          align-items: center
          display: flex
          flex-direction: column
          justify-content: center
          width: 20%
      slots:
        default:
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_net
          - component: oh-icon
            config:
              height: 40em
              icon: e3dc_wallbox
              style:
                margin-top: 1em
              visible: '=props.wallbox_charge ? (Number.parseFloat(items[props.wallbox_charge].state.split(" ")[0]) > 0) ? true : false : false'
          - component: oh-icon
            config:
              color: orange
              height: 40em
              icon: e3dc_home
              style:
                margin-top: 1em

Please let me know if you find any further issues.

@vbbaby3 : As you are the creator/maintainer of the widget, please take a look and see if you like it.

No problem, you can do what you want :wink:
I switched to home assistant

Works great with that improvement.

Wallbox is charging


The minor thing is the decimal places, but that´s no issue with the script. I´ll have to adjust the thing to item Javascript later to get rid of it.

No wallbox (=0W):