Send monthly reports

This commit is contained in:
Aart van Halteren
2023-02-21 22:43:22 +01:00
parent ccb8589a4b
commit 534e8dc294
4 changed files with 182 additions and 12 deletions

View File

@@ -26,10 +26,12 @@ class ReadingsMailer
# Fetch today's usage
usage_today = Reading.diff_on(date)
consumption_today, production_today = Reading.consumed_and_produced_for_diff(usage_today)
net_consumption_high, net_consumption_low = Reading.net_consumed_high_and_low_for_diff(usage_today)
# Calculate costs for oxxio and easy energy
c = Cost.new
oxxio_cost = c.oxxio_energy_cost(date.to_s,usage_today[:total_kwh_consumed_high]-usage_today[:total_kwh_produced_high], usage_today[:total_kwh_consumed_low]-usage_today[:total_kwh_produced_low])
oxxio_normaal_cost, oxxio_dal_cost = c.oxxio_energy_cost(date.to_s,net_consumption_high,net_consumption_low)
oxxio_cost = oxxio_normaal_cost+oxxio_dal_cost
easy_cost = c.easy_energy_cost_barplot(date) # side effect: generates a PNG
# rounding
oxxio_cost = oxxio_cost.round(2)
@@ -55,8 +57,8 @@ class ReadingsMailer
html_part do
content_type 'text/html; charset=UTF-8'
body "<h1>Summary for #{date}</h1>" +
"<p>Total kWH electricity consumed: #{usage_today[:total_kwh_consumed_high] + usage_today[:total_kwh_consumed_low]}</p>" +
"<p>Total kWH electricity produced: #{usage_today[:total_kwh_produced_high] + usage_today[:total_kwh_produced_low]}</p>" +
"<p>Total kWH electricity consumed: #{consumption_today}</p>" +
"<p>Total kWH electricity produced: #{production_today}</p>" +
"<p>Total m3 gas consumed: #{usage_today[:total_m3_gas_consumed]}</p>" +
"</br>" +
"<p>kWH cost (Oxxio): <b>EUR #{ oxxio_cost} </b></p>" +
@@ -70,5 +72,75 @@ class ReadingsMailer
mail.deliver!
end
# default is current year
def deliver_for_month(month, year=nil)
return if (month <1 || month >12)
year = Date.today.year unless year
date = Date.parse("%s%.2d01" % [year,month])
date_str = date.strftime("%B %Y")
# Read SMTP options from smtp.yml
smtp_opts = YAML::load(File.open('config/smtp.yml')).symbolize_keys
smtp_opts.merge!(SSL_OPTS) if smtp_opts[:ssl] && smtp_opts[:ssl_verify_mode].eql?("none")
# Fetch today's usage
usage_month = Reading.diff_in_month(date)
consumption_month, production_month = Reading.consumed_and_produced_for_diff(usage_month)
net_consumption_normaal, net_consumption_dal = Reading.net_consumed_high_and_low_for_diff(usage_month)
# Calculate costs for oxxio and easy energy
c = Cost.new
vat = 1 + c.vat_at(date)
# cost with VAT, but without energiebelasting.
oxxio_raw_cost = c.oxxio_energy_cost(date.to_s,net_consumption_normaal, net_consumption_dal, false).sum.round(2)
easy_energy_raw_cost = c.easy_energy_raw_cost_in_month(date).compact.sum.round(2)
# cost with VAT and with energiebelasting
oxxio_cost = c.oxxio_energy_cost(date.to_s,net_consumption_normaal, net_consumption_dal).sum.round(2)
easy_energy_cost = c.easy_energy_cost_in_month(date).compact.sum.round(2)
# opslag Easy Energy
easy_energy_opslag = (c.easy_energy_rate(date.to_s)*vat*(net_consumption_normaal + net_consumption_dal)).round(2)
mail = Mail.new do
delivery_method :smtp, smtp_opts
to 'a.t.van.halteren@vu.nl'
from 'SmartMeter <aart@van-halteren.net>'
subject "SmartMeter Month report for #{date_str}"
text_part do
body "Summary for #{date_str}\n
-------------------------------\n\n
Total kWH electricity consumed: #{consumption_month}\n
Total kWH electricity produced: #{production_month}\n
Total m3 gas consumed: #{usage_month[:total_m3_gas_consumed]}\n\n
Levering kWH cost (Oxxio) : EUR #{ oxxio_raw_cost }\n
Levering kWH cost (EasyEnergy): EUR #{ easy_energy_raw_cost }\n
Total kWH cost (Oxxio) : EUR #{ oxxio_cost }\n
Total kWH cost (EasyEnergy): EUR #{ easy_energy_cost }, inclusief opslag van EUR #{ easy_energy_opslag }\n
"
end
html_part do
content_type 'text/html; charset=UTF-8'
body "<h1>Summary for #{date_str}</h1>" +
"<p>Total kWH electricity consumed: #{consumption_month}</p>" +
"<p>Total kWH electricity produced: #{production_month}</p>" +
"<p>Total m3 gas consumed: #{usage_month[:total_m3_gas_consumed]}</p>" +
"</br>" +
"<p>Levering kWH cost (Oxxio): <b>EUR #{ oxxio_raw_cost} </b></p>" +
"<p>Levering kWH cost (EasyEnergy): <b>EUR #{ easy_energy_raw_cost} </b></p>" +
"<p>Total kWH cost (Oxxio): <b>EUR #{ oxxio_cost} </b></p>" +
"<p>Total kWH cost (EasyEnergy): <b>EUR #{ easy_energy_cost} </b>, inclusief opslag van <b>EUR #{ easy_energy_opslag }</b></p>"
end
end
mail.deliver!
end
end
end

View File

@@ -45,7 +45,9 @@ class Cost
end
# Assume: usage_kwh_cost, return_kwh_cost already includes vat
def add_tax(formatted_hour,usage_kwh,usage_kwh_cost,return_kwh, return_kwh_cost)
# calling this with only 'usage_kwh' parameter returns the tax amount for those kwh only.
#
def add_tax(formatted_hour,usage_kwh,usage_kwh_cost=0.0,return_kwh=0.0, return_kwh_cost=0.0)
return nil if (usage_kwh.nil? || usage_kwh_cost.nil? || return_kwh.nil? || return_kwh_cost.nil?)
vat = 1 + vat_at(Date.parse(formatted_hour))
@@ -117,6 +119,32 @@ class Cost
g.write("plots/easy_tariff_%s.png" % date.strftime("%F"))
end
# calculate the hourly cost (raw!) between start_hour and end_hour
# raw means: entsoe prices + VAT
def easy_energy_raw_hourly_cost_between(start_hour, end_hour)
begin_hour = start_hour
costs = []
while(begin_hour < end_hour) do
# get usage_kwh/return_kwh for one hour
begin_hour_plus1 = begin_hour.end_of_hour
hour_readings = Reading.where("created_at > :begin AND created_at < :end", {:begin => begin_hour, :end => begin_hour_plus1})
hour_diff = hour_readings.last ? hour_readings.last.diff(hour_readings.first) : UNKNOWN_READING
# helper to remove distiction between low/high tarif consumption
usage_kwh, return_kwh = Reading.consumed_and_produced_for_diff(hour_diff)
formatted_hour = begin_hour.strftime("%F %H")
# calculate RAW price (entsoe price + VAT)
kwh_price = entsoe.price_at(formatted_hour)*(1+vat_at(Date.parse(formatted_hour)))
hour_cost = (usage_kwh.nil? || return_kwh.nil? || kwh_price.nil?) ? nil : ((usage_kwh-return_kwh)*kwh_price)
costs << hour_cost
# do the next hour
begin_hour = begin_hour.advance(:hours => 1)
end
# give the result
costs
end
# calculate the hourly cost between start_hour and end_hour
def easy_energy_hourly_cost_between(start_hour, end_hour)
begin_hour = start_hour
@@ -158,6 +186,26 @@ class Cost
costs
end
# calculate leveringskosten (entsoe prices + VAT) in one month
# date = arbitrary day for the month
def easy_energy_raw_cost_in_month(date)
hour_start = date.beginning_of_month.in_time_zone(zone).beginning_of_day
hour_end = date.end_of_month.in_time_zone(zone).end_of_day
# can't get cost for the future
hour_end = Time.now if hour_end > Time.now
easy_energy_raw_hourly_cost_between(hour_start,hour_end)
end
def easy_energy_cost_in_month(date)
hour_start = date.beginning_of_month.in_time_zone(zone).beginning_of_day
hour_end = date.end_of_month.in_time_zone(zone).end_of_day
# can't get cost for the future
hour_end = Time.now if hour_end > Time.now
easy_energy_hourly_cost_between(hour_start,hour_end)
end
def easy_energy_hourly_cost_for(date)
hour_start = date.in_time_zone(zone).beginning_of_day
day_end = hour_start.advance(days: 1)
@@ -204,8 +252,9 @@ class Cost
high_tariff ? 0.47758 : 0.34165
end
end
def oxxio_energy_cost(formatted_hour, normaal_kwh, dal_kwh, year_shift=0)
# Optional: raw! (tarif + VAT) cost
def oxxio_energy_cost(formatted_hour, normaal_kwh, dal_kwh, with_tax=true, year_shift=0)
return nil if (normaal_kwh.nil? || dal_kwh.nil?)
#year = Date.parse(formatted_hour).year+year_shift
date = Date.parse(formatted_hour).advance(years: year_shift)
@@ -236,16 +285,24 @@ class Cost
normaal_kwh_cost = 0.47758*vat
dal_kwh_cost = 0.34165*vat
else
p "Not supported interval Oxxio for value: %d" % date.to_time.to_i
# catch-all, incase 'formated_hour' is outside any of the cases
normaal_kwh_cost = 0.0
dal_kwh_cost = 0.0
end
normaal_cost = add_tax(formatted_hour, normaal_kwh,normaal_kwh_cost,0,0) # return_kwh already accounted for
dal_cost = add_tax(formatted_hour, dal_kwh, dal_kwh_cost,0,0)
if with_tax
normaal_cost = add_tax(formatted_hour, normaal_kwh,normaal_kwh_cost) # return_kwh already accounted for
dal_cost = add_tax(formatted_hour, dal_kwh, dal_kwh_cost)
else
normaal_cost = normaal_kwh*normaal_kwh_cost
dal_cost = dal_kwh*dal_kwh_cost
end
# result
normaal_cost + dal_cost
return normaal_cost, dal_cost
end
#
# Entsoe
#

View File

@@ -67,6 +67,16 @@ class Reading < ActiveRecord::Base
def day(date)
Reading.where("created_at > :begin AND created_at < :end", { :begin => date.to_date.beginning_of_day, :end => date.to_date.end_of_day})
end
def in_month(date)
hour_start = date.beginning_of_month.beginning_of_day
hour_end = date.end_of_month.end_of_day
# can't get cost for the future
hour_end = Time.now if hour_end > Time.now
Reading.where("created_at > :begin AND created_at < :end", { :begin => hour_start, :end => hour_end})
end
def max_charge_kwh=(kwh)
@@max_charge_kwh = kwh
@@ -78,12 +88,19 @@ class Reading < ActiveRecord::Base
# do not make distinction between high and low consumption/production
def consumed_and_produced_for_diff(d)
usage_kwh = (d[:total_kwh_consumed_high].nil? || d[:total_kwh_consumed_low].nil?) ? nil : d[:total_kwh_consumed_high] + d[:total_kwh_consumed_low]
return_kwh = (d[:total_kwh_produced_high].nil? || d[:total_kwh_produced_low].nil?) ? nil : d[:total_kwh_produced_high] + d[:total_kwh_produced_low]
return usage_kwh, return_kwh
end
# calculate net_consumption (= consumption-production)
def net_consumed_high_and_low_for_diff(d)
net_consumed_high = (d[:total_kwh_consumed_high].nil? || d[:total_kwh_produced_high].nil?) ? nil : d[:total_kwh_consumed_high]-d[:total_kwh_produced_high]
net_consumed_low = (d[:total_kwh_consumed_low].nil? || d[:total_kwh_produced_low].nil?) ? nil : d[:total_kwh_consumed_low]-d[:total_kwh_produced_low]
return net_consumed_high, net_consumed_low
end
def diff_on(date)
readings_on = day(date)
@@ -106,5 +123,22 @@ class Reading < ActiveRecord::Base
{ :total_kwh_consumed_high => 0, :total_kwh_consumed_low => 0, :total_kwh_produced_high => 0, :total_kwh_produced_low => 0, :total_m3_gas_consumed => 0 }
end
end
def diff_in_month(date)
hour_start = date.beginning_of_month.beginning_of_day
hour_end = date.end_of_month.end_of_day
# can't get cost for the future
hour_end = Time.now if hour_end > Time.now
readings_in_month = Reading.where("created_at > :begin AND created_at < :end", { :begin => hour_start, :end => hour_end})
last = readings_in_month.last
if last
last.diff(readings_in_month.first)
else
{ :total_kwh_consumed_high => 0, :total_kwh_consumed_low => 0, :total_kwh_produced_high => 0, :total_kwh_produced_low => 0, :total_m3_gas_consumed => 0 }
end
end
end
end