Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/batcontrol/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,9 @@ def run(self):
1 - elapsed_in_current
)

this_logic_run = LogicFactory.create_logic(self.config, self.timezone)
this_logic_run = LogicFactory.create_logic(self.time_resolution,
self.config,
self.timezone)
Comment on lines +500 to +502
Comment on lines +500 to +502

# Create input for calculation
calc_input = CalculationInput(
Expand Down
5 changes: 2 additions & 3 deletions src/batcontrol/logic/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ class Logic:
""" Factory for logic classes. """
print_class_message = True
@staticmethod
def create_logic(config: dict, timezone) -> LogicInterface:
def create_logic(time_resolution_minutes: int, config: dict, timezone) -> LogicInterface:
""" Select and configure a logic class based on the given configuration """
request_type = config.get('type', 'default').lower()
interval_minutes = config.get('time_resolution_minutes', 60)
logic = None
if request_type == 'default':
if Logic.print_class_message:
logger.info('Using default logic')
Logic.print_class_message = False
logic = DefaultLogic(timezone, interval_minutes=interval_minutes)
logic = DefaultLogic(timezone, interval_minutes=time_resolution_minutes)
if config.get('battery_control_expert', None) is not None:
Comment on lines +13 to 22
battery_control_expert = config.get( 'battery_control_expert', {})
attribute_list = [
Expand Down
69 changes: 69 additions & 0 deletions tests/batcontrol/test_core.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Tests for core batcontrol functionality including MODE_LIMIT_BATTERY_CHARGE_RATE"""
import datetime
import pytest
from unittest.mock import MagicMock, patch

from batcontrol.core import (
Batcontrol,
MODE_LIMIT_BATTERY_CHARGE_RATE,
)
from batcontrol.logic.logic import Logic as LogicFactory


class TestModeLimitBatteryChargeRate:
Expand Down Expand Up @@ -287,5 +289,72 @@ def test_api_set_limit_applies_immediately_in_mode_8(
mock_inverter.set_mode_limit_battery_charge.assert_called_once_with(2000)


class TestTimeResolutionString:
"""Test that time_resolution_minutes provided as string (e.g. from Home Assistant) is handled correctly"""

@pytest.fixture
def base_mock_config(self):
"""Provide a minimal config for testing"""
return {
'timezone': 'Europe/Berlin',
'inverter': {
'type': 'dummy',
'max_grid_charge_rate': 5000,
'max_pv_charge_rate': 3000,
'min_pv_charge_rate': 100
},
'utility': {
'type': 'tibber',
'token': 'test_token'
},
'pvinstallations': [],
'consumption_forecast': {
'type': 'simple',
'value': 500
},
'battery_control': {
'max_charging_from_grid_limit': 0.8,
'min_price_difference': 0.05
},
'mqtt': {
'enabled': False
}
}

@pytest.mark.parametrize('resolution_str,expected_int', [('60', 60), ('15', 15)])
@patch('batcontrol.core.tariff_factory.create_tarif_provider')
@patch('batcontrol.core.inverter_factory.create_inverter')
@patch('batcontrol.core.solar_factory.create_solar_provider')
@patch('batcontrol.core.consumption_factory.create_consumption')
def test_string_time_resolution_initialises_without_error(
self, mock_consumption, mock_solar, mock_inverter_factory, mock_tariff,
base_mock_config, resolution_str, expected_int
):
"""Batcontrol must not crash when time_resolution_minutes is a string"""
mock_inverter = MagicMock()
mock_inverter.get_max_capacity = MagicMock(return_value=10000)
mock_inverter_factory.return_value = mock_inverter
mock_tariff.return_value = MagicMock()
mock_solar.return_value = MagicMock()
mock_consumption.return_value = MagicMock()

base_mock_config['time_resolution_minutes'] = resolution_str
bc = Batcontrol(base_mock_config)

assert isinstance(bc.time_resolution, int)
assert bc.time_resolution == expected_int

@pytest.mark.parametrize('resolution_str', ['60', '15'])
def test_logic_factory_accepts_string_resolution_as_int(self, resolution_str):
"""Logic factory must produce a valid logic instance when given an int resolution"""
logic = LogicFactory.create_logic(
int(resolution_str),
{'type': 'default'},
datetime.timezone.utc
)
assert logic is not None
assert logic.interval_minutes == int(resolution_str)
Comment on lines +347 to +356


if __name__ == '__main__':
pytest.main([__file__, '-v'])
Loading