Skip to content

Commit

Permalink
Cloud: Add support for Dashboard v2
Browse files Browse the repository at this point in the history
  • Loading branch information
ledermann committed Jan 11, 2025
1 parent 87d525c commit e1e6c21
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 81 deletions.
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,43 @@ puts Senec::Cloud::Dashboard[connection].find("123456").data
# {"stromerzeugung"=>{"wert"=>0.01, "einheit"=>"W"},
# ....

# Get the Technical Data of a specific system (by ID):
# Dashboard data can be requested in different versions, v1 and v2 are available, v1 is the default.
# To request the data in version 2, pass the version parameter to the `data` method:
puts Senec::Cloud::Dashboard[connection].first.data(version: 'v2')

# => {
# 'currently' => {
# 'powerGenerationInW' => 1.0e-05,
# 'powerConsumptionInW' => 1350.37,
# 'gridFeedInInW' => 1.0e-05,
# 'gridDrawInW' => 1321.26966059603,
# 'batteryChargeInW' => 1.0e-05,
# 'batteryDischargeInW' => 11.6411423841,
# 'batteryLevelInPercent' => 1.0e-05,
# 'selfSufficiencyInPercent' => 2.16,
# 'wallboxInW' => 1.0e-05
# },
# 'today' => {
# 'powerGenerationInWh' => 3.90625,
# 'powerConsumptionInWh' => 9119.14,
# 'gridFeedInInWh' => 0.0,
# 'gridDrawInWh' => 9011.71875,
# 'batteryChargeInWh' => 0.0,
# 'batteryDischargeInWh' => 107.421875,
# 'batteryLevelInPercent' => 1.0e-05,
# 'selfSufficiencyInPercent' => 1.18,
# 'wallboxInWh' => 0.0
# },
# 'timestamp' => '2025-01-11T06:45:09Z',
# 'electricVehicleConnected' => false
# }

# Get the Technical Data of a specific system (by ID):
puts Senec::Cloud::TechnicalData[connection].find("123456").data

# => {"systemOverview"=>{"systemId"=>123456, "productName"=>"SENEC.Home V3 hybrid duo", ...

# Get the Technical Data of first systems (without knowing the ID):

puts Senec::Cloud::TechnicalData[connection].first.data

# => {"systemOverview"=>{"systemId"=>123456, "productName"=>"SENEC.Home V3 hybrid duo", ...
Expand Down
35 changes: 23 additions & 12 deletions lib/senec/cloud/dashboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,31 @@
# connection = Senec::Cloud::Connection.new(username: '...', password: '...')
#
# # Get the data of a specific system:
# Dashboard[connection].find('123456')
# Dashboard[connection].find('123456').data
#
# # Get the data of the default system:
# Dashboard[connection].first
# Dashboard[connection].first.data
#
# By default, it returns v1 data. To get v2 data, use:
#
# Dashboard[connection].find('123456').data(version: 'v2')
# or
# Dashboard[connection].first.data(version: 'v2')
#
module Senec
module Cloud
class Dashboard
AVAILABLE_VERSIONS = %w[v1 v2].freeze
DEFAULT_VERSION = 'v1'.freeze

class Finder
def initialize(connection)
@connection = connection
end
attr_reader :connection

def find(system_id)
Dashboard.new(connection:, system_id:).tap(&:load_data)
Dashboard.new(connection:, system_id:)
end

def first
Expand All @@ -41,28 +50,30 @@ def initialize(connection: nil, system_id: nil, data: nil)
@system_id = system_id

# Useful for testing only
@data = data
@data = {
'v1' => data,
'v2' => data
}
end

def load_data
raise 'Data already present!' if @data

@system_id ||= connection.default_system_id
@data = fetch_data
def data(version: DEFAULT_VERSION)
@data ||= {}
@data[version] ||= fetch_data(version:)
end

attr_reader :system_id, :data
attr_reader :system_id

private

def get(path, params: nil)
@connection.get(path, params:)
end

def fetch_data
def fetch_data(version:)
raise ArgumentError unless AVAILABLE_VERSIONS.include?(version)
return unless system_id

get("/v1/senec/systems/#{system_id}/dashboard")
get("/#{version}/senec/systems/#{system_id}/dashboard")
end
end
end
Expand Down
136 changes: 87 additions & 49 deletions spec/lib/senec/cloud/dashboard_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,23 @@
)
end

describe '#initialize' do
it 'sets the connection' do
dashboard = described_class.new(connection:)
expect(dashboard.instance_variable_get(:@connection)).to eq(connection)
end

it 'sets the data' do
dashboard = described_class.new(data: { foo: 42 })
expect(dashboard.data).to eq(foo: 42)
end

it 'fails without connection or data' do
expect { described_class.new }.to raise_error(ArgumentError)
end

it 'fails when both connection and data given' do
expect do
described_class.new(connection:, data: { foo: 42 })
end.to raise_error(ArgumentError)
end

it 'fails when using loaddata after setting data' do
dashboard = described_class.new(data: { foo: 42 })

expect { dashboard.load_data }.to raise_error(
RuntimeError,
'Data already present!',
)
end
end

describe 'Finder' do
describe '#first', vcr: 'cloud/fetch-dashboard-default-system' do
describe '#first' do
subject(:dashboard) { finder.first }

it 'returns a Dashboard instance' do
it 'returns a Dashboard instance', vcr: 'cloud/fetch-dashboard-v1-default-system' do
expect(dashboard).to be_a(described_class)
end

it 'uses the default_system_id' do
it 'uses the default_system_id', vcr: 'cloud/fetch-dashboard-v1-default-system' do
expect(dashboard.system_id).to eq(connection.default_system_id)
end

it 'fetches the data' do
expect(dashboard.data).to include('aktuell', 'heute')
end
end

describe '#find', vcr: 'cloud/fetch-dashboard-specific-system' do
describe '#find' do
subject(:dashboard) { finder.find(system_id) }

context 'with VALID system_id', vcr: 'cloud/fetch-dashboard-specific-system' do
context 'with VALID system_id' do
let(:system_id) { ENV.fetch('SENEC_SYSTEM_ID') }

it 'returns a Dashboard instance' do
Expand All @@ -69,32 +34,105 @@
it 'uses the provided system_id' do
expect(dashboard.system_id).to eq(system_id)
end
end

context 'with INVALID system_id', vcr: 'cloud/fetch-dashboard-invalid-system' do
let(:system_id) { 123_456 }

it 'fails' do
expect { dashboard.data }.to raise_error(Senec::Cloud::Error, 'Error 401')
end
end
end

describe '#initialize' do
it 'sets the connection' do
dashboard = described_class.new(connection:)
expect(dashboard.instance_variable_get(:@connection)).to eq(connection)
end

it 'sets the data' do
dashboard = described_class.new(data: { foo: 42 })
expect(dashboard.data).to eq(foo: 42)
end

it 'fails without connection or data' do
expect { described_class.new }.to raise_error(ArgumentError)
end

it 'fails when both connection and data given' do
expect do
described_class.new(connection:, data: { foo: 42 })
end.to raise_error(ArgumentError)
end
end

describe '#data' do
let(:dashboard) { finder.first }

context 'with default version of 1', vcr: 'cloud/fetch-dashboard-v1-default-system' do
subject(:data) { dashboard.data }

it 'fetches the data' do
expect(dashboard.data).to include('aktuell', 'heute')
expect(data).to include('aktuell', 'heute')
end

it 'fetches data with keys' do
expected_keys = %w[
stromerzeugung stromverbrauch
netzeinspeisung netzbezug
speicherbeladung speicherentnahme
stromerzeugung
stromverbrauch
netzeinspeisung
netzbezug
speicherbeladung
speicherentnahme
speicherfuellstand
autarkie
wallbox
]

%w[aktuell heute].each do |key|
expect(dashboard.data[key].keys).to match_array(expected_keys)
expect(data[key].keys).to match_array(expected_keys)
end
end
end

context 'with INVALID system_id', vcr: 'cloud/fetch-dashboard-invalid-system' do
let(:system_id) { 123_456 }
context 'with version 2', vcr: 'cloud/fetch-dashboard-v2-default-system' do
subject(:data) { dashboard.data(version: 'v2') }

it 'fails' do
expect { dashboard }.to raise_error(Senec::Cloud::Error, 'Error 401')
it 'fetches the data' do
expect(data).to include('currently', 'today')
end

it 'fetches currently data' do
expect(data['currently'].keys).to match_array(
%w[
batteryChargeInW
batteryDischargeInW
batteryLevelInPercent
gridDrawInW
gridFeedInInW
powerConsumptionInW
powerGenerationInW
selfSufficiencyInPercent
wallboxInW
],
)
end

it 'fetches today data' do
expect(data['today'].keys).to match_array(
%w[
batteryChargeInWh
batteryDischargeInWh
batteryLevelInPercent
gridDrawInWh
gridFeedInInWh
powerConsumptionInWh
powerGenerationInWh
selfSufficiencyInPercent
wallboxInWh
],
)
end
end
end
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e1e6c21

Please sign in to comment.