Closed PeterH24x7 closed 1 year ago
Here's the updated full source code that is currently under test. Testing via others would be valued.
**<<< 5-Mar-23 - all updates as described in code header comments for Release 5 - this is currently under final test. >>>
# THIS IS FINAL DRAFT CODE FOR TESTING
# -----------------------------------------------------------------------------
#
# SOLAR ANALYTICS integration for Home Assistant Energy Monitoring
#
# Developed by Peter Hormann and with thanks to Glen S and Roving-Ronin for their contributions.
#
# Created: 7-Jan-2022
#
# Last updated: 5-Mar-2023
#
# Updates:
# 16-Jan-2022 - INITIAL RELEASE 1
# - Added power reporting (last 5 minute average).
# 16-Jul-2022 - RELEASE 2
# - Enabled more accurate and more frequent 1 minute power reporting
# - Added 5 minute energy updates for all channels
# - Moved sa_site_id to secrets.yaml.
# 20-Aug-2022 - RELEASE 3
# - Added some error checking to deal with daily energy total errors when the
# data get is 'unavailable' or 'unknown'
# - Removed "force_update: true" - where the data get may be 'unavailable'
# - Removed "state_class: measurement" and "device_class: energy" for rest platform
# sensors (not applicable)
# 10-Feb-2023 - RELEASE 4
# - Added "unique_id" to each sensor entity (including existing) to enable HA
# Lovelace based settings changes.
# - Added new "Total Energy Generated Expected" sensor to provide daily cumulative
# expected energy (as per the Solar Analytics portal).
# - Added kW sensors as conversions of their respective W conterpart (note: not
# required for energy entities as of HA 2023.2).
# - Where HA EM is used for cost calculations added "Todays Energy Total Net Cost"
# (import cost minus feed-in revenue).
# - For those on variable energy rates (e.g. Amber) created new sensors "Todays Energy
# Import Rate" and "Todays Energy Export Rate".
# 5-Mar-2023 - RELEASE 5 THIS DRAFT
# - Added "unique_id" to each REST sensor.
# - Implemented token authentication with timer based refresh using username/password.
# - Updated REST sensors to use token authentication.
# - Changed sa_live_site_data to 60s scan_interval (avoid repeated update at 30s
# interval where data actually only changes every minute - some users may consider
# changing back to 30s to reduce the delay to update).
# - Fixed sa_live_site_data, sa_consumption_power, sa_generation_power and
# sa_import_export_power which needed extra error recovery when REST get on the hour
# (e.g. 14:00) from SA is empty. Issue started with HA 2023.3 update and Lovelac
# support for changing power units from Watts to kilo-Watts
# >>> Author recommendation to wait for further updates to HA before using this feature.
# - Fixed state_attr for various sensors time_stamp when source sensor is unknown or
# unavailable.
# - Added time_stamp attribute to sa_todays_energy_total_net_cost,
# sa_todays_energy_import_rate and sa_todays_energy_export_rate.
# - Added 3x new sensors sa_todays_energy_consumed_solar (Wh),
# sa_todays_generated_consumed_percentage (%) and
# sa_todays_consumed_generated_percentage (%).
# - Added automation sa_refresh_all_rest_gets to manually force refesh of all REST
# data gets.
#
# This code is provided as-is and without any warrantee or guarantees.
# Further public contributions and enhancements are welcomed.
#
# Installation:
# 1. Copy this yaml file to the HA config directory.
# 2. Include this file name as a package in the HA configuration.yaml.
# For example -
# packages:
# pack_1: !include solar_analytics.yaml
# 3. Define sa_username, sa_password and sa_site_id (e.g. 36304) in the secrets.yaml file.
# 4. Using developer tools check configuration and restart. From States, review the
# attributes for sa_data_by_5min to identify the device specific channel names and
# update/delete the sa_todays_... xyz sensors to suit.
# 6. Add the new energy sensors from 4. above to the HA Energy Manager as "Monitor
# Individual Devices". Go to HA settings and then search for “Energy Configuration”
# to make the changes.
# 7. Also in HA Energy Configuration, enter the details for
# > Grid consumption - as sa_todays_energy_imported
# > Return to grid - as sa_todays_energy_exported
# > Solar production - as sa_todays_energy_generated_total
# 8. If all working is correctly, the Energy dashboard should start populating data within
# a couple of hours.
# 9. If HA EM "Grid Consumption" and "Return to grid" are configured with energy pricing
# details, then the net cost, import rate and export rate should work without further
# config. Else delete these sensors from the configuration.
# 10.Enjoy!
#
# If you find my work useful, please buy me a coffee ... thank you!
# https://www.buymeacoffee.com/peter24x7
#
# Reference for more information and updates:
# https://github.com/PeterH24x7/-Solar-Analytics-integration-for-Home-Assistant-Energy-monitoring
#
# -----------------------------------------------------------------------------
#
# Define the Solar Analytics site_id value is required in URLs used in gets below - e.g. 36304.
# See the Solar Analytics user portal to find your site id or see the collected sa_site_list
# attributes as defined below.
#
input_text:
sa_site_id:
initial: !secret sa_site_id
# -----------------------------------------------------------------------------
#
# Define a timer to track when the SA authentication token needs to be renewed
# - set by the automation sa_token_expiry_update.
# Added 25-Feb-23 to replace repeated username and password authentication.
#
timer:
sa_token_expiry_timer:
duration: 0
icon: mdi:clock-check
restore: true
#
# Define automations to set the sa_token_expiry_timer when the token is renewed/checked.
# - On sa_token_expiry_timer expiry the rest sa_auth_token is called.
# Added 25-Feb-23 to replace repeated username and password authentication.
#
automation:
- id: 'sa_token_expiry_update'
alias: SA Token Expiry update
description: 'On token refresh, restart the time with new time to expiry.'
trigger:
- platform: state
entity_id:
- sensor.sa_auth_token
for:
hours: 0
minutes: 0
seconds: 5
condition: []
action:
- service: timer.start
data:
duration: '{{ states(''sensor.sa_auth_token'') | int }}'
target:
entity_id: timer.sa_token_expiry_timer
mode: single
- id: 'sa_token_refresh_on_timer_expiry'
alias: SA Token refresh on Timer expiry
description: 'Refresh the token with the timer expires.'
trigger:
- platform: state
entity_id:
- timer.sa_token_expiry_timer
to: idle
for:
hours: 0
minutes: 0
seconds: 5
condition: []
action:
- service: homeassistant.update_entity
data: {}
target:
entity_id: sensor.sa_auth_token
mode: single
- id: 'sa_refresh_all_rest_gets'
alias: SA Refresh all REST data gets
description: "Automation that can be manually run to refresh all REST data gets."
trigger: []
condition: []
action:
- service: homeassistant.update_entity
data: {}
target:
entity_id:
- sensor.sa_site_list
- sensor.sa_status
- sensor.sa_data_by_5min
- sensor.sa_live_site_data
- sensor.sa_data_by_hour
mode: single
sensor:
#
# Solar Analytics - rest get of latest token using username and password credentials.
# - Auto updates every 2 hours, else on sa_token_expiry_timer via automation.
# Added 25-Feb-23 to replace repeated username and password authentication.
#
- platform: rest
name: sa_auth_token
unique_id: sa_auth_token
resource: https://portal.solaranalytics.com.au/api/v3/token
username: !secret sa_username
password: !secret sa_password
authentication: basic
value_template: "{{ value_json['duration'] }}"
json_attributes:
- "token"
- "expires"
scan_interval: 7200
# -----------------------------------------------------------------------------
#
# Solar Analytics - get the site details associated with the given user account login.
# Updated every 24 hours.
#
# note: assumes only a single device per account, there could be a second device (e.g. replaced
# device due to fault/upgrade) at $.data.[1]
#
- platform: rest
name: sa_site_list
unique_id: sa_site_list
resource: https://portal.solaranalytics.com.au/api/v3/site_list?hardware=true&capacity=true&subscription=true
headers:
Authorization: >
{{ 'Bearer ' ~ state_attr('sensor.sa_auth_token','token') }}
value_template: "{{ now() }}"
json_attributes_path: "$.data.[0]"
json_attributes:
- "e_status"
- "fault_class"
- "fault_id*"
- "has_pv"
- "mer_status"
- "overall_status"
- "retailer_user"
- "s_cli_site_name"
- "site_id"
- "site_inactive"
- "capacity"
- "devices"
- "sub_type"
scan_interval: 62400
# -----------------------------------------------------------------------------
#
# Solar Analytics - get the site status for the specified site_id.
# Updated every hour.
#
- platform: rest
name: sa_status
unique_id: sa_status
resource_template: https://portal.solaranalytics.com.au/api/v3/site_status/{{ states('input_text.sa_site_id') }}
headers:
Authorization: >
{{ 'Bearer ' ~ state_attr('sensor.sa_auth_token','token') }}
value_template: "{{ value_json['data']['mer_status'] }}"
json_attributes_path: "$.data"
json_attributes:
- "dashboard_status"
- "event_id"
- "event_list"
- "fault_status"
- "mer_percentage"
- "mer_status"
- "mer_text"
scan_interval: 3600
- platform: template
sensors:
sa_dashboard_status:
friendly_name: "Dashboard Status"
unique_id: sa_dashboard_status
value_template: "{{ state_attr('sensor.sa_status', 'dashboard_status') }}"
sa_mer_status:
friendly_name: "System Status"
unique_id: sa_mer_status
value_template: "{{ state_attr('sensor.sa_status', 'mer_status') }}"
sa_mer_percentage:
friendly_name: "PV Performance"
unique_id: sa_mer_percentage
value_template: "{{ state_attr('sensor.sa_status', 'mer_percentage') }}"
unit_of_measurement: "%"
# -----------------------------------------------------------------------------
#
# Solar Analytics - get 5 minute energy data.
# Used to calculate today's cumulative total energy for consumed, generated, imported and exported
# energy as used by the HA Energy module.
# Updated every 5 minutes.
# Note: the previous version calculated sa_consumption_power, sa_generation_power and sa_import_export_power (as an average over
# 5 minutes) are now directly sourced using get sa_live_site_data sensor further down below.
# 15-Jul-22 - added trunc=false to include decimal components of 5 minute date (default is true).
# Also added air conditioner, EV, hot water and stove-oven energy loads as shown in the attributes of sa_data_by_5min.
# These channels should be added/edited/deleted to match the specific device set-up.
#
- platform: rest
name: sa_data_by_5min
unique_id: sa_data_by_5min
resource_template: https://portal.solaranalytics.com.au/api/v2/site_data/{{ states('input_text.sa_site_id') }}?all=true&gran=minute&trunc=false&power=false&tstart={{ now().strftime("%Y%m%d") }}&tend={{ now().strftime("%Y%m%d") }}
headers:
Authorization: >
{{ 'Bearer ' ~ state_attr('sensor.sa_auth_token','token') }}
value_template: >-
{% set most_recent_sensor_data = value_json['data'] | rejectattr('energy_consumed', 'equalto', None) | list | last %}
{{ most_recent_sensor_data.t_stamp }}
json_attributes:
- "data"
scan_interval: 300
- platform: template
sensors:
sa_todays_energy_consumed_total:
friendly_name: Total Energy Consumed
unique_id: sa_todays_energy_consumed_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'energy_consumed', 'equalto', None ) | sum( attribute='energy_consumed' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_consumed_total' ) }}
{% endif %}
icon_template: mdi:home-lightning-bolt-outline
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_consumed_total", "time_stamp" ) }}
{% endif %}
sa_todays_energy_generated_total:
friendly_name: Total Energy Generated
unique_id: sa_todays_energy_generated_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'energy_generated', 'equalto', None ) | sum( attribute='energy_generated' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_generated_total' ) }}
{% endif %}
icon_template: mdi:solar-power
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_generated_total", "time_stamp" ) }}
{% endif %}
sa_todays_air_conditioner_total:
friendly_name: Heating Cooling Energy
unique_id: sa_todays_air_conditioner_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'load_air_conditioner', 'equalto', None ) | sum( attribute='load_air_conditioner' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_air_conditioner_total' ) }}
{% endif %}
icon_template: mdi:hvac
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_air_conditioner_total", "time_stamp" ) }}
{% endif %}
sa_todays_electric_vehicle_total:
friendly_name: Electric Vehicle Energy
unique_id: sa_todays_electric_vehicle_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'load_ev_charger', 'equalto', None ) | sum( attribute='load_ev_charger' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_electric_vehicle_total' ) }}
{% endif %}
icon_template: mdi:car-electric
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_electric_vehicle_total", "time_stamp" ) }}
{% endif %}
sa_todays_hot_water_total:
friendly_name: Hot Water Energy
unique_id: sa_todays_hot_water_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'load_hot_water', 'equalto', None ) | sum( attribute='load_hot_water' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_hot_water_total' ) }}
{% endif %}
icon_template: mdi:shower-head
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_hot_water_total", "time_stamp" ) }}
{% endif %}
sa_todays_stove_oven_total:
friendly_name: Stove Oven Energy
unique_id: sa_todays_stove_oven_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.attributes.data | rejectattr( 'load_stove', 'equalto', None ) | sum( attribute='load_stove' ) | int }}
{% else %}
{{ states( 'sensor.sa_todays_stove_oven_total' ) }}
{% endif %}
icon_template: mdi:stove
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_stove_oven_total", "time_stamp" ) }}
{% endif %}
sa_todays_energy_imported:
friendly_name: Total Energy Imported
unique_id: sa_todays_energy_imported
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{% set energy = namespace( imported = 0 ) %}
{% for sensor_data in states.sensor.sa_data_by_5min.attributes.data | rejectattr('energy_consumed', 'equalto', None) if sensor_data.energy_consumed > sensor_data.energy_generated %}
{% set energy.imported = energy.imported + sensor_data.energy_consumed - sensor_data.energy_generated %}
{% endfor %}
{{ energy.imported | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_imported' ) }}
{% endif %}
icon_template: mdi:transmission-tower-export
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_imported", "time_stamp" ) }}
{% endif %}
sa_todays_energy_exported:
friendly_name: Total Energy Exported
unique_id: sa_todays_energy_exported
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{% set energy = namespace( exported = 0 ) %}
{% for sensor_data in states.sensor.sa_data_by_5min.attributes.data | rejectattr('energy_generated', 'equalto', None) if sensor_data.energy_generated > sensor_data.energy_consumed %}
{% set energy.exported = energy.exported + sensor_data.energy_generated - sensor_data.energy_consumed %}
{% endfor %}
{{ energy.exported | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_exported' ) }}
{% endif %}
icon_template: mdi:transmission-tower-import
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_exported", "time_stamp" ) }}
{% endif %}
# -----------------------------------------------------------------------------
#
# Solar Energy Consumed as kWh, % of generated, and % of of consumed.
# Added 4-Mar-23 - initial version.
#
sa_todays_energy_consumed_solar:
friendly_name: Total Energy Consumed Solar
unique_id: sa_todays_energy_consumed_solar
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{% set energy = namespace( solar_consumed = 0 ) %}
{% for sensor_data in states.sensor.sa_data_by_5min.attributes.data | rejectattr('energy_generated', 'equalto', None) %}
{% if sensor_data.energy_generated >= sensor_data.energy_consumed %}
{% set energy.solar_consumed = energy.solar_consumed + sensor_data.energy_consumed %}
{% else %}
{% set energy.solar_consumed = energy.solar_consumed + sensor_data.energy_generated %}
{% endif %}
{% endfor %}
{{ energy.solar_consumed | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_consumed_solar' ) }}
{% endif %}
icon_template: mdi:solar-power
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_5min' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_5min.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_consumed_solar", "time_stamp" ) }}
{% endif %}
# Percentage of generated energy that has been consumed.
sa_todays_generated_consumed_percentage:
friendly_name: Percentage Generated Energy Consumed
unique_id: sa_todays_generated_consumed_percentage
unit_of_measurement: "%"
value_template: >
{% if states( 'sensor.sa_todays_energy_generated_total') | float > 0 %}
{{ ((states('sensor.sa_todays_energy_consumed_solar') | float) / (states( 'sensor.sa_todays_energy_generated_total') | float) * 100) | round(1) }}
{% else %}
0.0
{% endif %}
icon_template: mdi:solar-power
attribute_templates:
time_stamp: >
{{ state_attr( "sensor.sa_todays_energy_consumed_solar", "time_stamp" ) }}
# Percentage of consumed energy that has been generated.
sa_todays_consumed_generated_percentage:
friendly_name: Percentage Consumed Energy Generated
unique_id: sa_todays_consumed_generated_percentage
unit_of_measurement: "%"
value_template: >
{% if states( 'sensor.sa_todays_energy_consumed_total') | float > 0 %}
{{ ((states('sensor.sa_todays_energy_consumed_solar') | float) / (states( 'sensor.sa_todays_energy_consumed_total') | float) * 100) | round(1) }}
{% else %}
0.0
{% endif %}
icon_template: mdi:solar-power
attribute_templates:
time_stamp: >
{{ state_attr( "sensor.sa_todays_energy_consumed_solar", "time_stamp" ) }}
# -----------------------------------------------------------------------------
#
# Solar Analytics - get live per minute power data - added 9-Jul-22.
# Used to generate most recent power information.
#
# 4-Mar-23 - changed to 60 seconds (could be 30s to improve power management responsiveness - if required).
#
# Note: this is not a published API 'get', but is used by the SA portal to display minute to minute power for a site.
#
- platform: rest
name: sa_live_site_data
unique_id: sa_live_site_data
resource_template: https://portal.solaranalytics.com.au/api/v3/live_site_data?site_id={{ states('input_text.sa_site_id') }}&last_six=true
headers:
Authorization: >
{{ 'Bearer ' ~ state_attr('sensor.sa_auth_token','token') }}
value_template: >-
{% set most_recent_sensor_data = value_json['data'] | list | last %}
{{ as_timestamp(most_recent_sensor_data.t_stamp) | timestamp_custom ('%Y-%m-%d %H:%M:%S')}}
json_attributes:
- "data"
scan_interval: 60
#
# IMPORTANT UPDATE NOTES
# sa_live_site_data and the derived power sensors below seem to be experiencing issues with the update to HA 2023.3 and the Lovelace power
# unit options enabling a change of sensor Watts to kilo-Watts. Not sure why but REST get would now fail ('unknown') on the hour (e.g. 14:00)
# due to no data. All works okay without change when set to default Watts, but not kilo-Watts for derived sensors below.
# Set to kilo-Watts, the power sensors below would be either 'unknown' or zero, rather than recovering with the last !env_var
# known value (per the yaml code).
# Hoping this is fixed by future HA updates.
#
# The following were various options tried for sa_live_site_data value_template above, but didn't work.
# {% if value_json['data'] is defined %} ok {% else %} unknown {% endif %}
#
# {% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
# {{ most_recent_sensor_data.consumed | float( default=states('sensor.sa_consumption_power') | float) | int }}
#
- platform: template
sensors:
sa_consumption_power:
friendly_name: Power Consumption
unique_id: sa_consumption_power
unit_of_measurement: "W"
value_template: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ most_recent_sensor_data.consumed | int }}
{% else %}
{{ states( 'sensor.sa_consumption_power' ) }}
{% endif %}
icon_template: mdi:home-lightning-bolt-outline
device_class: "power"
attribute_templates:
state_class: measurement
time_stamp: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ as_timestamp(most_recent_sensor_data.t_stamp) | timestamp_custom ('%Y-%m-%d %H:%M:%S')}}
{% else %}
{{ state_attr( "sensor.sa_consumption_power", "time_stamp" ) }}
{% endif %}
sa_generation_power:
friendly_name: Power Generation
unique_id: sa_generation_power
unit_of_measurement: "W"
value_template: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ most_recent_sensor_data.generated | int }}
{% else %}
{{ states( 'sensor.sa_generation_power' ) }}
{% endif %}
icon_template: mdi:solar-power
device_class: "power"
attribute_templates:
state_class: measurement
time_stamp: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ as_timestamp(most_recent_sensor_data.t_stamp) | timestamp_custom ('%Y-%m-%d %H:%M:%S')}}
{% else %}
{{ state_attr( "sensor.sa_generation_power", "time_stamp" ) }}
{% endif %}
sa_import_export_power:
friendly_name: Power Import Export
unique_id: sa_import_export_power
unit_of_measurement: "W"
value_template: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ (most_recent_sensor_data.consumed | int) - (most_recent_sensor_data.generated | int) }}
{% else %}
{{ states( 'sensor.sa_import_export_power' ) }}
{% endif %}
icon_template: mdi:transmission-tower
device_class: "power"
attribute_templates:
state_class: measurement
time_stamp: >
{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %}
{% set most_recent_sensor_data = states.sensor.sa_live_site_data.attributes.data | list | last %}
{{ as_timestamp(most_recent_sensor_data.t_stamp) | timestamp_custom ('%Y-%m-%d %H:%M:%S')}}
{% else %}
{{ state_attr( "sensor.sa_import_export_power", "time_stamp" ) }}
{% endif %}
# -----------------------------------------------------------------------------
#
# Solar Analytics - get hourly cumulative expected energy data for the day.
# Used to report today's total expected generated energy to the most recent hour - a time lagging report of expected, and not a forecast.
# Notes: this is not a published API 'get', but is used by the SA portal.
# The get also retrieves the daily total for the most recent hour for generated and consumed energy; not required given 5 min updates of the same above.
# Checked every 30 minutes for changes in the last hour.
#
# Total Energy Generated Expected
# Added 24-Jan-23 - initial version.
#
- platform: rest
name: sa_data_by_hour
unique_id: sa_data_by_hour
resource_template: https://portal.solaranalytics.com.au/api/v2/site_data/{{ states('input_text.sa_site_id') }}?all=false&gran=day&losses=true&power=false&trunc=false&tstart={{ now().strftime("%Y%m%d") }}&tend={{ now().strftime("%Y%m%d") }}
headers:
Authorization: >
{{ 'Bearer ' ~ state_attr('sensor.sa_auth_token','token') }}
value_template: >-
{{ as_timestamp(now()) | timestamp_custom ('%Y-%m-%d %H:%M:%S') }}
# The rest get timestamp is only the current date - rather use the current date-time of the get.
# {{ as_timestamp(value_json['data'][0].t_stamp) | timestamp_custom ('%Y-%m-%d %H:%M:%S') }}
json_attributes:
- "data"
scan_interval: 1800
- platform: template
sensors:
sa_todays_energy_generated_expected_total:
friendly_name: Total Energy Generated Expected
unique_id: sa_todays_energy_generated_expected_total
unit_of_measurement: "Wh"
value_template: >
{% if states( 'sensor.sa_data_by_hour' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_hour.attributes.data[0].energy_expected | int }}
{% else %}
{{ states( 'sensor.sa_todays_energy_generated_expected_total' ) }}
{% endif %}
icon_template: mdi:solar-power
device_class: "energy"
attribute_templates:
state_class: total_increasing
time_stamp: >
{% if states( 'sensor.sa_data_by_hour' ) not in ("unavailable", "unknown") %}
{{ states.sensor.sa_data_by_hour.state }}
{% else %}
{{ state_attr( "sensor.sa_todays_energy_generated_expected_total", "time_stamp" ) }}
{% endif %}
# -----------------------------------------------------------------------------
#
# Convert Solar Analtics Watt (W) measurements to kilo-Watts (kW).
# Added 24-Jan-23 - initial version.
# TO BE DELETED 4-Mar-23 - functionality replaced by HA 2023.3 (but with some issues to resolve)
#
# Useful for display purposes if required.
#
#
# Power conversions
#
# sa_consumption_power_kw:
# friendly_name: Power Consumption - kW
# unique_id: sa_consumption_power_kw
# unit_of_measurement: "kW"
# value_template: >
# {{ (states('sensor.sa_consumption_power') | float / 1000) | round(2) }}
# icon_template: mdi:home-lightning-bolt-outline
# device_class: "power"
# Use of state_class results in history being collected and the creation of a near duplicate data set in the HA database.
# attribute_templates:
# state_class: measurement
# sa_generation_power_kw:
# friendly_name: Power Generation - kW
# unique_id: sa_generation_power_kw
# unit_of_measurement: "kW"
# value_template: >
# {{ (states('sensor.sa_generation_power') | float / 1000) | round(2) }}
# icon_template: mdi:solar-power
# device_class: "power"
# attribute_templates:
# state_class: measurement
# sa_import_export_power_kw:
# friendly_name: Power Import Export - kW
# unique_id: sa_import_export_power_kw
# unit_of_measurement: "kW"
# value_template: >
# {{ (states('sensor.sa_import_export_power') | float / 1000) | round(2) }}
# icon_template: mdi:transmission-tower
# device_class: "power"
# attribute_templates:
# state_class: measurement
# -----------------------------------------------------------------------------
#
# Home Assistant Energy Management extras
# Added 10-Feb-23 - initial version
#
#
# Todays Energy Total Net Cost
#
# HA EM automatically creates two entities respectively derived from the sa_todays_energy_imported and sa_todays_energy_exported
# > sa_todays_energy_imported_cost - cost of imported energy
# > sa_todays_energy_exported_compensation - revenue from exported energy
# This new entity provides the net cost (imported cost minus exported revenue)
#
sa_todays_energy_total_net_cost:
friendly_name: Todays Energy Total Net Cost
unique_id: sa_todays_energy_total_net_cost
unit_of_measurement: "AUD"
device_class: "monetary"
icon_template: mdi:cash
value_template: >
{{ (states.sensor.sa_todays_energy_imported_cost.state | float) - (states.sensor.sa_todays_energy_exported_compensation.state | float) }}
attribute_templates:
state_class: total_increasing
time_stamp: >
{{ state_attr( "sensor.sa_todays_energy_imported", "time_stamp" ) }}
#
# Todays Energy Import Rate
#
# Useful for those on a variable electricity general usage rate configured in the HA Energy Manager (e.g. peak/off-peak/shoulder, Amber Energy wholesale spot market pricing).
# The average import rate $/kWh = cumulative imported energy kWh divided by the cumulative imported energy cost $. Resets to zero at midnight.
# The sensor sa_todays_energy_imported_cost is created by the HA Energy Manager when appropriate pricing is set-up for grid consumption.
#
sa_todays_energy_import_rate:
friendly_name: Todays Energy Import Rate
unique_id: sa_todays_energy_import_rate
unit_of_measurement: "$/kWh"
device_class: "monetary"
icon_template: mdi:cash-plus
value_template: >
{% if (states.sensor.sa_todays_energy_imported.state | float) != 0.0 %}
{{ ((states.sensor.sa_todays_energy_imported_cost.state | float) / (states.sensor.sa_todays_energy_imported.state | float)) | round(3) }}
{% else %}
{{ 0.0 }}
{% endif %}
attribute_templates:
state_class: measure
time_stamp: >
{{ state_attr( "sensor.sa_todays_energy_imported", "time_stamp" ) }}
#
# Todays Energy Export Rate
#
# Useful for those on a variable electricity feed-in (FIT) rate configured in the HA Energy Manager (e.g. peak/off-peak/shoulder, Amber Energy wholesale spot market pricing).
# The average export rate $/kWh = cumulative exported energy kWh divided by the cumulative exported energy revenue $. Resets to zero at midnight.
# The sensor sa_todays_energy_exported_compensation is created by the HA Energy Manager when appropriate pricing is set-up for energy returned to the grid.
#
sa_todays_energy_export_rate:
friendly_name: Todays Energy Export Rate
unique_id: sa_todays_energy_export_rate
unit_of_measurement: "$/kWh"
device_class: "monetary"
icon_template: mdi:cash-plus
value_template: >
{% if (states.sensor.sa_todays_energy_exported.state | float) != 0.0 %}
{{ ((states.sensor.sa_todays_energy_exported_compensation.state | float) / (states.sensor.sa_todays_energy_exported.state | float)) | round(3) }}
{% else %}
{{ 0.0 }}
{% endif %}
attribute_templates:
state_class: measure
time_stamp: >
{{ state_attr( "sensor.sa_todays_energy_exported", "time_stamp" ) }}
# -----------------------------------------------------------------------------
Hi Peter,
Seems to be working on two instances I have (using same SA data feed info). Only problem I still get is I never get any data for the import/export rate and the total net cost, all of them just show 'unavailable'.
A few other state class type messages again in the logs (fresh docker HA image run up)
TemplateError('TypeError: state_attr() missing 1 required positional argument: 'name'') while processing template 'Template("{% if states( 'sensor.sa_live_site_data' ) not in ("unavailable", "unknown") %} {{ states.sensor.sa_live_site_data.state }} {% else %} {{ state_attr( "sensor.sa_consumption_power.time_stamp" ) }} {% endif %}")' for attribute 'time_stamp' in entity 'sensor.sa_consumption_power'
10:00:13 PM – (ERROR) helpers/template_entity.py - message first occurred at 1:00:12 PM and shows up 30 times Entity sensor.sa_todays_energy_generated_total from integration template has state class total_increasing, but its state is not strictly increasing. Triggered by state 86230 (86232.0) with last_updated set to 2023-02-27T08:49:42.836196+00:00. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+template%22
8:50:10 PM – (WARNING) Sensor - message first occurred at 7:50:10 PM and shows up 2 times Sensor sensor.sa_todays_energy_imported has device class energy, state class None and unit Wh thus indicating it has a numeric value; however, it has the non-numeric value: unknown (<class 'str'>); Please update your configuration if your entity is manually configured, otherwise create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+template%22
12:49:43 PM – (WARNING) Sensor - message first occurred at 12:49:43 PM and shows up 7 times Found unexpected state_class None for sensor.sa_todays_energy_imported
12:49:42 PM – (WARNING) Energy - message first occurred at 12:49:42 PM and shows up 2 times
Hi @Roving-Ronin - sorry it's my issue. For some reason my HA EM decided to put a suffix of "_2" at the end of the imported_cost and exported_compensation sensors and I don't know how to safely fix the issue. I've edited 3x lines of code above such as the following to fix. Should be okay for you now.
Will have a closer look at the errors later - I think we need to just live with the "total_increasing" issue.
Change:
{{ (states.sensor.sa_todays_energy_imported_cost_2.state | float) - (states.sensor.sa_todays_energy_exported_compensation_2.state | float) }}
to:
{{ (states.sensor.sa_todays_energy_imported_cost.state | float) - (states.sensor.sa_todays_energy_exported_compensation.state | float) }}
@PeterH24x7 The _2 just means that you've re-run the yaml and for some reason its decided you've already got a states.sensor.sa_todays_energy_imported_cost and decided to make a new instance and call it states.sensor.sa_todays_energy_imported_cost_2.
Personally I'd just remove the SA yaml (or rename temporarily to a different file extension), then restart HA. Then go into Settings/Device&Services/Entities and delete all states.sensor.sa_todays_energy_imported_cost and states.sensor.sa_todays_energy_imported_cost_2. Then go to Developer Tools / Statistic adn check there also and repeat. Then rename your SA yaml back and restart HA.
@Roving-Ronin - thanks for the tip and I will have a think about it. The "sa_todays_energy_imported_cost" and "sa_todays_energy_exported_compensation" are derived and created by HA's Energy Management module so without knowing what might happen (including any historic data) I'm reluctant to make the change for sake of just making my sensor list look clean (and the code inconsistencies experienced above). I guess I could make a back up first and test my recovery strategy.
@PeterH24x7 With the last release of code and this new version, I've got it on my PROD and also run up another image of HA to test it. The DEV instance just has your SA yaml and the electricity tariffs yamls (I normally used, but aren't for this instances Energy Dashboard).
Both instances are the same though in outcome. Both have sensor.sa_todays_energy_export_rate, sensor.sa_todays_energy_import_rate and sensor.sa_todays_energy_total_net_cost created.
Neither though have this sa_todays_energy_imported_cost or sa_todays_energy_exported_compensation that you mentioned as being auto created.
Energy Management is configured with:
Is there something your instance has added, that mine doesn't ? Is there somewhere akin to the 5min entity that might show this data to allow cross checking if my setup is actually receiving it via REST ?
scratching head...
BTW: Have you ever looked at submitting this as proper integration for HA? Would be easier for none IT users to then add, and automatically get alerts to update and then update with a few clicks?
Hi @Roving-Ronin - scratching my head too. Both sa_todays_energy_imported_cost and sa_todays_energy_exported_compensation are not part of my SA integration, rather as you say auto created by the HA EM. I found them by accident one day when reviewing hidden entities (select show hidden entities on the filter) - see image 1 below. You will see they are not associated with a "Template" integration, rather the "Energy" integration. While they were/are hidden (and remain hidden), they can nonetheless be found and added to the lovelace dashboard - see image 2. For reference, my Energy Manager is configured as per Images 3 (import), 4 (export) and 5 (generated).
Regarding submitting a proper integration for HA, I haven't quite gotten to that point. However, with the most recent token-based update (something I've been meaning to fix for some time) I think the integration is essentially complete - excepting some of the issues/errors you've flagged above. I will have a closer look at what's involved. I can't imagine it's too difficult, but there is the complication of needing to configure the 3 to 6 channels. I have shared my work with Solar Analytics and I was hoping they might consider productising it for themselves and providing requisite support, rather than me take on all the responsibility.
Image 1]
Image 2]
Image 3]
Image 4]
Image 5]
In reviewing Image 1 above, I had a look at whether I could delete the Energy related sensors - answer no, as delete is greyed-out. HOWEVER, it did allow me to rename the sensor and update!!! Having renormalised my solar_analytics.yaml file, my problem is fixed it seems. Under advanced settings I note the Entity Status is fixed as Hidden.
@PeterH24x7,
Apologies yes I have the _exported_compensation and _imported_cost (hidden) entities.
However issue still remains that _energy_import_rate and energy_total_net_cost never receive any information. Whilst energy_export_rate has received some info (it doesn't have the warning ! against it in status field), however checking the history of it, the last update it got was yesterday and all updates have been $0.00 k/Wh.
If this is all supposed to be getting pulled down from SA using REST...? I have a plan setup in SA portal for all my TOU / Import and Export costs...so not sure why HA's are not getting/updating with this info...?
@PeterH24x7 Forgot to add, I do notice your Energy Dashboard is setup slightly differently, with your 'Use an entity with current price' referring to a yaml that references Amber. Is there something in that... or should these rates be pulled via REST from SA...
Hi @Roving-Ronin - sa_todays_energy_imported_cost and sa_todays_energy_exported_compensation are defined and calculated by the HA EM - as marked in the image below - they don't come from SA except they are calculated by HA based on SA sourced kWh multiplied by a unit rate (as you've shown above). For Amber this rate (and the feed-in rate) changes every 5 mins.
Can I suggest you try creating a helper entity with your unit rate AUD/kWh (fixed or changing according to a calendar/schedule if required for peak/off-peak rates) and use that entity in the "Use an entity with current rate" option above?
Each REST get repeatedly use the username/password rather than token-based authentication and periodic token-based refresh. Will provide improved security by not exposing username/password as often.