Skip to content

Instantly share code, notes, and snippets.

@arcreative
Created February 3, 2023 19:38
Show Gist options
  • Save arcreative/baf7174d7c5574a515c5cf4bb5877bbb to your computer and use it in GitHub Desktop.
Save arcreative/baf7174d7c5574a515c5cf4bb5877bbb to your computer and use it in GitHub Desktop.
SunPower PVS5/PVS6 Proxy to Home Assistant Scraper
FROM alpine:latest
RUN apk add --update --no-cache ruby -y
ADD scraper.rb .
CMD ["ruby", "scraper.rb"]
require 'net/http'
require 'json'
$verbose = %w[1 true TRUE].include?(ENV.fetch('VERBOSE', 'FALSE'))
# Sleep to let other docker containers to boot, e.g. when using docker-compose
sleep ENV.fetch('INITIAL_SLEEP_SECONDS', 0).to_i
# Gets data from SunPower PVS5/PVS6 proxy
def get_data
uri = URI("#{ENV.fetch('SUNPOWER_PROXY_HOST')}/cgi-bin/dl_cgi?Command=DeviceList")
response = Net::HTTP.get(uri)
JSON.parse(response)
end
# Posts data to HA
def post_data(entity_id, value, friendly_name, unit = "kW")
# Show action if VERBOSE is set
puts "#{friendly_name} (#{unit}): #{value}" if $verbose
# Need to do the more complex HTTP call since we want to ignore TLS validity for local network hosts
uri = URI("#{ENV.fetch('HA_HOST')}/api/states/#{entity_id}")
req = Net::HTTP::Post.new(uri.path)
req.body = {
state: value,
attributes: {
entity_id: entity_id,
state_class: "measurement",
unit_of_measurement: unit,
device_class: "power",
friendly_name: friendly_name,
}
}.to_json
req['Authorization'] = "Bearer #{ENV.fetch('HA_ACCESS_TOKEN')}"
req['Content-Type'] = 'application/json'
Net::HTTP.start(
uri.host, uri.port,
use_ssl: uri.scheme == 'https',
verify_mode: OpenSSL::SSL::VERIFY_NONE,
) do |https|
https.request(req)
end
end
# Main loop
while true
data = get_data
# Find the inverter and CT data elements
inverters = data['devices'].filter { |i| i['DEVICE_TYPE'] == 'Inverter' }
grid_usage_meters = data['devices'].filter { |i| i['TYPE'] == 'PVS5-METER-C' }
# Calculate the production/consumption
production_kw = inverters.map { |i| i['p_3phsum_kw'].to_f }.reduce(0, &:+).round(4)
grid_usage_kw = grid_usage_meters.map { |i| i['p_3phsum_kw'].to_f }.reduce(0, &:+).round(4)
home_usage_kw = (production_kw + grid_usage_kw).round(4)
# Post to HA API
post_data('sensor.solar_power_current_production', production_kw, "Solar Power Current Production")
post_data('sensor.solar_power_current_grid_usage', grid_usage_kw, "Solar Power Current Grid Usage")
post_data('sensor.solar_power_current_home_usage', home_usage_kw, "Solar Power Current Net Home Usage")
# Sleep for an interval
sleep ENV.fetch('SLEEP_SECONDS', 30).to_i
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment