Enphase Envoy - D7 firmware with JWT - A Different Approach

Hi @Steve61,

The example you have provided is the power sensors.

For power sensors, I chose to use integer because the number being exposed by the envoy is a whole number without a decimal point.
I feel that changing it to float adds unnecessary detail.

This approach changes for the energy sensors as they are exposed as floating point numbers.
My code reflects that.

The only potential loss of accuracy is where I added round(2) which rounds a longer floating point number to 2 decimal points. I was happy with the slight loss of accuracy. If you wish, you are welcome to remove the round(2) from the code to retain full accuracy.

Here is a graph of my energy export today for example

1 Like

Iā€™m not seeing any errors in my own sensors. Are you sure you have copied my code as it is written without any modifications?

Thanks. I donā€™t want to go back and experiment because I seem to be getting consistent data and donā€™t want to mess up that history. I will try with some temporary but identical-source sensors.

I had a play in the Template page (Developer Tools) and couldnā€™t see any issues, but that doesnā€™t always reflect the real thing accurately.

Yes sort of. I changed the names of the sensors (I use a different naming structure) and added the /1000 to convert to kW where the data was in watts. That could have caused an issue going from 1,567-watts to 1.567kW - where I assume a floating point was needed, not an integer.

Ah now I understand what you are trying to do.
If you take an integer figure like 2500 W and then divide it by 1000 which moves the decimal point over to the left by 3 places to make a floating point number like 2.500 kW, then yes you will need to covert the output to floating point in order to see the .500 kW after the decimal point. If you leave the output as integer, it will only show 2 kW and you will miss any detail after the decimal point.

In my code, the only time I have done any conversion is for the energy sensors to convert Wh to kWh.
The reason why I did that was so that I could feed energy in kWh to the utility meter integration which is then used by energy dashboard which seems to work best with kWh.

In your case, you have chosen to modify the power sensors from W to kW. Doing so will have knock on effects to any downstream sensors that use those as a source. You just need to make sure all 5 power sensors are in kW and output as floating point.

That sums it up. I just wanted everything expressed the same way instead of a mix of watts and kilowatts. Itā€™s still rounded to three decimal points so I get the same detail.

Is there a way to deal with spikes in the calculations? I had an instance the other day where the consumption power data threw in a 500-kilowatt spike, which threw out the data for the day. I think this is an issue with the Envoy, because the first one I had was replaced because it was reporting erratic consumption spikes that made it look like I was running a steel mill. (Hopefully I donā€™t have another - this would be out of warranty)

I can see you run the ā€œmaxā€ function but if I could block consumption power over say 20kW that would deal with spikes.

1 Like

I see those occasional weirdo figures too. Iā€™ve been zeroing them out manually but building a trap in to the integration would be awesome.

Iā€™ve not had much success in manually changing the data - I assume you are referring to the ā€œstatisticsā€ page in the Developer Tools?

Edit: Iā€™ve just had a 15,000kWh spike in my daily consumption data in the past hour. Itā€™s an anomaly of some sort because it doesnā€™t show up in the child sensors that use that spike data to calculate energy costs.

Iā€™ve been able to find and change the anomaly every time except for one. Iā€™ve even looked through the database for that weird value. I canā€™t find it. June 8th from 7am to 8am. It haunts me :slight_smile:

1 Like

I think I might have worked out the spike - and no I canā€™t get rid of the two I have.

I think it was caused when I took shortcuts, by only reloading the REST entries and not doing a full HA restart. This led to a 0.0 entry in the lifetime consumption figure. It then reverted to the actual reading (some 15,000 kwh) which convinced the utility meter sensor I had just used that amount. It reverts to normal readings at midnight, but I am guessing that because the ā€œStatisticsā€ data only samples in 5 minute intervals, you canā€™t correct something that falls in between the normal sample time.

This is excellent, exactly what I was looking for. Iā€™ve had it running for several days to compare to Enlighten and it is accurate:


image

1 Like

Has anyone else experienced blips after updated to 2023.8.4? Since updating to 2023.08.4 this afternoon, I now have these sections of no data like in this screenshot

image

The default timeout for rest integration is 10 seconds.

timeout integer (optional, default: 10)

Defines max time to wait data from the endpoint.

I donā€™t believe that has changed with the latest update.

Please check your log for something like this

Logger: homeassistant.components.rest.data
Source: components/rest/data.py:116 
Integration: RESTful (documentation, issues) 
First occurred: August 25, 2023 at 6:50:16 PM (9 occurrences) 
Last logged: 2:06:40 AM

Timeout while fetching data: https://192.168.1.114/ivp/meters/readings

Have you tried this possible solution as well?

Cheers for the suggestion, however, not entirely what I was thinking.
In an effort to mimic the Enphase App, Energy tab, Iā€™ve stumbled upon the HACS card - energy-period-selector-plus. GitHub - flixlix/energy-period-selector-plus: An upgraded Energy Date Selection Card for Home Assistant, with added customizability, while maintaining the Energy Dashboard's original design.


It looks quite nice, however, changing the date doesnā€™t affect the Enphase graph span or offsetā€¦
Has anyone here persisted through this issue and found a solution?
It would be great to work through this and add more functionality to @del13r 's great work.

Here is the frontend code:

square: false
type: grid
cards:
  - type: custom:energy-period-selector-plus
    card_background: false
    today_button: true
    prev_next_buttons: true
    compare_button_type: icon
    today_button_type: text
    period_buttons:
      - day
      - week
      - month
      - year
      - custom
  - type: custom:apexcharts-card
    graph_span: 24h
    span:
      start: day
    stacked: true
    header:
      show: true
      title: Enphase Solar
    all_series_config:
      group_by:
        func: avg
        duration: 3min
      type: column
    series:
      - entity: sensor.power_production
        name: Produced
        color: '#01B4DE'
      - entity: sensor.power_consumption
        transform: return x *-1 ;
        name: Consumed
        color: '#F37320'
      - entity: sensor.power_net
        name: Imported/Exported
        color: '#545456'
columns: 1

1 Like

Hi @Klaneos, I saw something here that might be a good lead

1 Like

Hi @Klaneous,

I got it working. I am able to choose the day of the graph now.

Note: Recorder only holds 10 days of data by default.
See https://www.home-assistant.io/integrations/recorder/#purge_keep_days

today graph

Yesterday graph

What did I do?

Added input_select helpers

input_select:
  statistics_chart_range:
    initial: day
    options:
      - day
      - week
      - month
      - year

input_datetime:
  statistics_chart_date:
    has_date: true
    has_time: false

Added template sensors

template:
  - sensor:
      - name: statistics_chart_grouping
        unique_id: statistics_chart_grouping
        state: |
          {% set grouping={
            'day':'h',
            'week':'d',
            'month':'d',
            'year':'d'}
          %}
          {% set range = states('input_select.statistics_chart_range') %}
          {{ grouping[range] }}
          
      - name: statistics_chart_span
        unique_id: statistics_chart_span
        state: |
          {% macro last_date_in_range(date_str,range) -%}
            {% set date=as_timestamp(date_str)|timestamp_local()|as_datetime() -%}
            {% if range=='day' -%}
              {% set end_date=date -%}
            {% elif range=='week' -%}
              {% set end_date=date + timedelta(days=(6-date.weekday())) -%}
            {% elif range=='month' -%}
              {% set month=(date.month % 12) + 1 -%}
              {% set year=date.year+iif(date.month==12,1,0) -%}
              {% set end_date=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d')-timedelta(days=1) -%}
            {% elif range=='year' -%}
              {% set end_date=strptime(date.year|string + '-12-31', '%Y-%m-%d') -%}
            {% endif -%}
            {{ as_timestamp(end_date)|timestamp_local() -}}
          {% endmacro -%}
          {% set range = states('input_select.statistics_chart_range') %}
          {% set date_str = states('input_datetime.statistics_chart_date') %}
          {% set spans={
            'day':'24',
            'week':'7',
            'month':(last_date_in_range (strptime(date_str, '%Y-%m-%d'),range)|as_datetime()).day,
            'year':last_date_in_range (strptime(date_str, '%Y-%m-%d'),range)|as_timestamp(0)|timestamp_custom('%j',0)|int +1}
          %}
          {{ spans[range] }}
          
      - name: statistics_chart_period
        unique_id: statistics_chart_period
        state: |
          {% set periods={
            'day':'hour',
            'week':'day',
            'month':'day',
            'year':'day'}
          %}
          {% set range = states('input_select.statistics_chart_range') %}
          {{ periods[range] }}
          
  - sensor:
      - name: statistics_chart_offset
        unique_id: statistics_chart_offset
        state: |
          {% macro last_date_in_range(date_str,range) -%}
            {% set date=as_timestamp(date_str)|timestamp_local()|as_datetime() -%}
            {% if range=='day' -%}
              {% set end_date = date + timedelta(days=1) -%}
            {% elif range=='week' -%}
              {% set end_date=date + timedelta(days=(6-date.weekday())) -%}
            {% elif range=='month' -%}
              {% set month=(date.month % 12) + 1 -%}
              {% set year=date.year+iif(date.month==12,1,0) -%}
              {% set end_date=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d')-timedelta(days=1) -%}
            {% elif range=='year' -%}
              {% set end_date=strptime(date.year|string + '-12-31', '%Y-%m-%d') -%}
            {% endif -%}
            {{ as_timestamp(end_date) -}}
          {% endmacro -%}
          {% set range = states('input_select.statistics_chart_range') %}
          {% set date_str = states('input_datetime.statistics_chart_date') %}
          {% set offset = last_date_in_range(strptime(date_str, '%Y-%m-%d'),range)|int - now()|as_timestamp(0) -%}
          {% if states('sensor.statistics_chart_grouping') == 'h' %}
            {% set offset=(offset/60/60)|round(method='ceil') %}
          {% else %}
            {% set offset=(offset/60/60/24)|round(method='ceil') + 1 %}
          {% endif %}
          {{ iif(offset >= 0,'+','') }}{{ offset }}{{ states('sensor.statistics_chart_grouping') }}

added script to
/config/scripts.yaml

alias: statistics_chart_date_browse
sequence:
  - service: input_datetime.set_datetime
    target:
      entity_id: input_datetime.statistics_chart_date
    data:
      datetime: >
        {% set range = states('input_select.statistics_chart_range') %}

        {% set date = states('input_datetime.statistics_chart_date')|as_datetime
        %}

        {% set direction = direction %}

        {% if range == 'day' %}
          {% if direction > 0 %}
            {% set target = date + timedelta(days=1) %}
          {% else %}
            {% set target = date - timedelta(days=1) %}
          {% endif %}
        {% elif range == 'week' -%}
          {% if direction > 0 %}
            {% set target = date + timedelta(days=7) %}
          {% else %}
            {% set target = date - timedelta(days=7) %}
          {% endif %}
        {% elif range == 'month' -%}
          {% if direction > 0 %}
            {% set month=((date.month) % 12) + 1 %}
            {% set year=date.year+iif(date.month==12,1,0) %}
            {% set target=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d') -%}
          {% else %}
            {% set month=((date.month - 2) % 12) + 1 %}
            {% set year=date.year+iif(date.month==1,-1,0) %}
            {% set target=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d') -%}
          {% endif %}
        {% elif range=='year' -%}
          {% if direction > 0 %}
            {% set target=strptime((date.year|int + 1)|string + '-01-01', '%Y-%m-%d') -%}
          {% else %}
            {% set target=strptime((date.year|int - 1)|string + '-01-01', '%Y-%m-%d') -%}
          {% endif %}
        {% endif -%}

        {% if direction == 0 %}
            {% set target = now() %}
        {% endif -%}

        {{ target }}
mode: single
icon: mdi:arrow-right-bold-outline

Created a new dashboard view

  - theme: Backend-selected
    title: Enphase
    path: enphase
    icon: mdi:solar-power
    type: sidebar
    badges: []
    cards:
      - type: custom:config-template-card
        variables:
          offset: states['sensor.statistics_chart_offset'].state
          span: states['sensor.statistics_chart_span'].state
          period: states['sensor.statistics_chart_period'].state
          grouping: states['sensor.statistics_chart_grouping'].state
        entities:
          - sensor.statistics_chart_offset
          - sensor.statistics_chart_span
          - sensor.statistics_chart_period
          - sensor.statistics_chart_grouping
        card:
          type: custom:apexcharts-card
          graph_span: ${span + period}
          span:
            offset: ${offset}
          stacked: true
          header:
            show: false
            title: Total Power
          series:
            - entity: sensor.power_production
              type: column
              name: Produced
              color: '#01B4DE'
              group_by:
                func: avg
                duration: 5min
            - entity: sensor.power_consumption
              transform: return x *-1 ;
              type: column
              name: Consumed
              color: '#F37320'
              group_by:
                func: avg
                duration: 5min
            - entity: sensor.power_net
              type: column
              name: Imported/Exported
              color: '#545456'
              group_by:
                func: avg
                duration: 5min
          yaxis:
            - min: -7000
              max: 7000
              decimals: 0
      - type: custom:mod-card
        card:
          type: horizontal-stack
          cards:
            - type: entities
              entities:
                - entity: input_datetime.statistics_chart_date
                  name: date
            - type: entities
              entities:
                - entity: input_select.statistics_chart_range
                  name: period
            - type: glance
              show_name: false
              show_state: false
              entities:
                - entity: script.statistics_chart_date_browse
                  icon: mdi:chevron-left
                  tap_action:
                    action: call-service
                    service: script.statistics_chart_date_browse
                    data:
                      direction: -1
                - entity: script.statistics_chart_date_browse
                  icon: mdi:chevron-down
                  tap_action:
                    action: call-service
                    service: script.statistics_chart_date_browse
                    data:
                      direction: 0
                - entity: script.statistics_chart_date_browse
                  icon: mdi:chevron-right
                  tap_action:
                    action: call-service
                    service: script.statistics_chart_date_browse
                    data:
                      direction: 1
2 Likes

This is very nice! Great job @del13r :clap:

1 Like

@del13r Thanks for all this info. Quick question that I havenā€™t yet seen the answer to (although havent read all the links above). I have 3 phase power which is getting monitored by the Enphase. I can see in the app under LIVE STATUS the current consumption (power) per phase but I am yet to find out whether it is possible to get that info into HA? Anyone know if that is possible?

Hello,
Yes the per phase information is available in the channels section of the json data.
Each channel is a phase.

Have you tried clicking on https://envoy.local/ivp/meters/readings and seeing what information you are presented with?