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
15 changes: 14 additions & 1 deletion packages/helpermodules/subdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,14 @@ def on_connect(self, client: mqtt.Client, userdata, flags: dict, rc: int):
("openWB/internal_io/#", 2),
("openWB/optional/#", 2),
("openWB/counter/#", 2),
("openWB/consumer/#", 2),
("openWB/consumer/get/#", 2),
("openWB/consumer/+/config", 2),
("openWB/consumer/+/extra_meter", 2),
("openWB/consumer/+/module", 2),
("openWB/consumer/+/usage", 2),
("openWB/consumer/+/get/#", 2),
("openWB/consumer/+/set/#", 2),
("openWB/consumer/+/control_parameter/#", 2),
("openWB/command/command_completed", 2),
("openWB/internal_chargepoint/#", 2),
# MQTT Bridge Topics vor "openWB/system/+" abonnieren, damit sie auch vor
Expand Down Expand Up @@ -1261,6 +1268,9 @@ def process_consumer_topic(self, client: mqtt.Client, var: Dict[str, Consumer],
con.device_descriptor.configuration_factory, consumer_config)
var["consumer"+index].module = con.create_consumer(config)
var["consumer"+index].data.module = config
client.subscribe(f"openWB/consumer/{index}/module/simulation", 2)
self.processing_counter.add_task()
Pub().pub("openWB/system/subdata_initialized", True)
except Exception:
fault_str = (
f"Verbraucher {index}: Modul für "
Expand All @@ -1270,6 +1280,9 @@ def process_consumer_topic(self, client: mqtt.Client, var: Dict[str, Consumer],
var["consumer"+index].data.get.fault_state = 2
var["consumer"+index].data.get.fault_str = fault_str
pub_system_message({}, fault_str, MessageType.ERROR)
elif re.search("openWB/consumer/[0-9]+/module/simulation$", msg.topic) is not None:
var["consumer"+index].module.sim_counter.data = dataclass_from_dict(
SimCounterState, decode_payload(msg.payload))
elif re.search("openWB/consumer/[0-9]+/config", msg.topic) is not None:
self.set_json_payload_class(var["consumer"+index].data.config, msg)
elif re.search("openWB/consumer/[0-9]+/get", msg.topic) is not None:
Expand Down
21 changes: 9 additions & 12 deletions packages/modules/common/configurable_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from helpermodules import timecheck
from helpermodules.pub import Pub
from modules.common.abstract_consumer import CurrentValues
from modules.common.abstract_device import AbstractCounter
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
Expand Down Expand Up @@ -79,10 +78,15 @@ def update(self):
imported, exported = self.peak_filter.check_values(
consumer_state.power,
consumer_state.imported,
consumer_state.exported if consumer_state.exported is not None else 0)
if imported != consumer_state.imported or exported != consumer_state.exported:
consumer_state.imported = imported
consumer_state.exported = exported
consumer_state.exported)
if consumer_state.imported is None or consumer_state.exported is None:
simulated_imported, simulated_exported = self.sim_counter.sim_count(consumer_state.power)
if consumer_state.imported is None:
imported = simulated_imported
if consumer_state.exported is None:
exported = simulated_exported
consumer_state.imported = imported
consumer_state.exported = exported
self.store.set(consumer_state)

def set_power_limit(self, power_limit: float, data: SetLimitData) -> None:
Expand All @@ -100,10 +104,3 @@ def switch_off(self) -> None:
def send_values(self, values: CurrentValues) -> None:
with SingleComponentUpdateContext(self.fault_state):
self.module_send_values(values)


def dependency_injection_devices_components(component: AbstractCounter):
component.sim_counter.component_type = ComponentType.CONSUMER.value
component.store = get_component_value_store(ComponentType.CONSUMER.value, component.component_config.id)
component.fault_state.component_info.type = ComponentType.CONSUMER.value
return component
2 changes: 1 addition & 1 deletion packages/modules/common/simcount/_simcounter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple, Optional
from typing import Optional, Tuple

from modules.common.component_type import ComponentType, special_to_general_type_mapping
from modules.common.simcount._simcount import sim_count
Expand Down
9 changes: 6 additions & 3 deletions packages/modules/common/simcount/_simcounter_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from control import data
from helpermodules import pub
from helpermodules.utils.topic_parser import get_second_index
from helpermodules.utils.topic_parser import get_index, get_second_index
from modules.common.component_type import ComponentType, type_to_topic_mapping
from modules.common.simcount.simcounter_state import SimCounterState

Expand All @@ -29,8 +29,11 @@ def save(self, topic: str, state: SimCounterState):

def restore_last_energy(topic: str, value: str, component_type: ComponentType):
try:
module_type = type_to_topic_mapping(component_type.value)
module = getattr(data.data, f"{module_type}_data")[f"{module_type}{get_second_index(topic)}"].data.get
if component_type == ComponentType.CONSUMER:
module = data.data.consumer_data[f"consumer{get_index(topic)}"].data.get
else:
module_type = type_to_topic_mapping(component_type.value)
module = getattr(data.data, f"{module_type}_data")[f"{module_type}{get_second_index(topic)}"].data.get
return getattr(module, value)
except AttributeError:
if (value == "imported" and component_type == ComponentType.INVERTER):
Expand Down
17 changes: 17 additions & 0 deletions packages/modules/common/simcount/_simcounter_store_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from types import SimpleNamespace

import pytest

from control import data
from modules.common.component_type import ComponentType
from modules.common.simcount._simcounter_store import restore_last_energy


@pytest.mark.parametrize(("value", "expected"), [("imported", 1200), ("exported", 25)])
def test_restore_last_energy_reads_consumer_state(monkeypatch, value, expected):
consumer = SimpleNamespace(data=SimpleNamespace(get=SimpleNamespace(imported=1200, exported=25)))
monkeypatch.setattr(data, "data", SimpleNamespace(consumer_data={"consumer3": consumer}))

actual = restore_last_energy("openWB/set/consumer/3/module/", value, ComponentType.CONSUMER)

assert actual == expected
9 changes: 1 addition & 8 deletions packages/modules/consumers/askoma/askoheat/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,27 @@
from typing import Optional
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.modbus import ModbusDataType, ModbusTcpClient_
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.askoma.askoheat.config import Askoheat

log = logging.getLogger(__name__)


def create_consumer(config: Askoheat):
client: Optional[ModbusTcpClient_] = None
sim_counter: Optional[SimCounterConsumer] = None

def initializer():
nonlocal client, sim_counter
nonlocal client
client = ModbusTcpClient_(config.configuration.ip_address, config.configuration.port)
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

def error_handler() -> None:
initializer()

def update() -> ConsumerState:
power = client.read_input_registers(110, ModbusDataType.INT_16, unit=config.configuration.modbus_id)
imported, exported = sim_counter.sim_count(power)
return ConsumerState(
power=power,
imported=imported,
exported=exported,
temperatures=[client.read_input_registers(638, ModbusDataType.INT_16, unit=config.configuration.modbus_id)]
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,25 @@

from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.modbus import ModbusDataType, ModbusTcpClient_
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.daikin.daikin_homehub_altherma.config import DaikinAltherma

log = logging.getLogger(__name__)


def create_consumer(config: DaikinAltherma):
client: Optional[ModbusTcpClient_] = None
sim_counter: Optional[SimCounterConsumer] = None

def initializer():
nonlocal client, sim_counter
nonlocal client
client = ModbusTcpClient_(config.configuration.ip_address, config.configuration.port)
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

def update() -> ConsumerState:
# Reg 51: Stromverbrauch Wärmepumpe, Pow16 (raw = kW * 100)
power = client.read_input_registers(
50, ModbusDataType.INT_16, unit=config.configuration.modbus_id) * 10
imported, exported = sim_counter.sim_count(power)
return ConsumerState(power=power, imported=imported, exported=exported)
return ConsumerState(power=power)

def switch_on() -> None:
# Reg 56: Smart-Grid-Modus, 2 = Empfehlung ein (Boost)
Expand Down
9 changes: 1 addition & 8 deletions packages/modules/consumers/ego/ego/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@
from modules.common.abstract_consumer import CurrentValues
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.modbus import ModbusDataType, ModbusTcpClient_
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.ego.ego.config import Ego

log = logging.getLogger(__name__)


def create_consumer(config: Ego):
client: Optional[ModbusTcpClient_] = None
sim_counter: Optional[SimCounterConsumer] = None

def initializer():
nonlocal client, sim_counter
nonlocal client
client = ModbusTcpClient_(config.configuration.ip_address, config.configuration.port)
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

def send_values(values: CurrentValues) -> None:
unit = config.configuration.modbus_id
Expand All @@ -40,12 +36,9 @@ def update() -> ConsumerState:
power += client.read_holding_registers(4128, ModbusDataType.UINT_16, unit=unit)
if status & 0x04:
power += client.read_holding_registers(4160, ModbusDataType.UINT_16, unit=unit)
imported, exported = sim_counter.sim_count(power)

return ConsumerState(
power=power,
imported=imported,
exported=exported
)

def set_power_limit(power_limit: Optional[float], data: SetLimitData) -> None:
Expand Down
14 changes: 2 additions & 12 deletions packages/modules/consumers/ems_esp/ems_esp/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,20 @@
from modules.common import req
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.ems_esp.ems_esp.config import EmsEsp

log = logging.getLogger(__name__)


def create_consumer(config: EmsEsp):
session: Optional[req.CustomSession] = None
sim_counter: Optional[SimCounterConsumer] = None
base_url: str = ""
headers: dict = {}

def initializer():
nonlocal session, sim_counter, base_url, headers
nonlocal session, base_url, headers
session = req.get_http_session()
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)
# "heatpump" ist der EMS-ESP-Gerätename für eine reine Wärmepumpe; Hybridanlagen mit
# Gas-/Ölkessel laufen zB unter "boiler" (https://emsesp.org/All-Entities).
base_url = f"http://{config.configuration.ip_address}/api/heatpump"
Expand All @@ -48,13 +44,7 @@ def update() -> ConsumerState:
f"Entity 'hpcurrpower' nicht in der Antwort von {base_url} enthalten - "
f"vorhandene Werte: {list(values.keys())}")
power = float(values["hpcurrpower"])
imported, exported = sim_counter.sim_count(power)

return ConsumerState(
power=power,
imported=imported,
exported=exported
)
return ConsumerState(power=power)

def set_power_limit(power_limit: Optional[float], data: SetLimitData) -> None:
# pvmaxcomp wirkt nur bei aktivem SG-Ready-Boost (Eingang 4); bei SUSPENDABLE_TUNABLE ruft
Expand Down
6 changes: 1 addition & 5 deletions packages/modules/consumers/generic/dac/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
import logging
from typing import Optional
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.modbus import ModbusTcpClient_
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.generic.dac.config import Dac
from modules.consumers.generic.dac.model import Model

Expand All @@ -14,12 +12,10 @@

def create_consumer(config: Dac):
client: Optional[ModbusTcpClient_] = None
sim_counter: Optional[SimCounterConsumer] = None

def initializer():
nonlocal client, sim_counter
nonlocal client
client = ModbusTcpClient_(config.configuration.ip_address, config.configuration.port)
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

def error_handler() -> None:
initializer()
Expand Down
16 changes: 6 additions & 10 deletions packages/modules/consumers/generic/http/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from modules.common import req
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.simcount._simcounter import SimCounterConsumer
from helpermodules.utils._get_default import get_default
from modules.consumers.generic.http.config import Http
from modules.devices.generic.http.api import (create_post_function, create_request_function,
create_request_function_array)
Expand All @@ -16,7 +15,6 @@

def create_consumer(config: Http):
session: Optional[req.CustomSession] = None
sim_counter: Optional[SimCounterConsumer] = None
get_power: Optional[Callable] = None
get_imported: Optional[Callable] = None
get_exported: Optional[Callable] = None
Expand All @@ -27,11 +25,10 @@ def create_consumer(config: Http):
post_switch_off: Optional[Callable] = None

def initializer():
nonlocal session, sim_counter
nonlocal session
nonlocal get_power, get_imported, get_exported, get_currents, get_temperatures
nonlocal post_set_power_limit, post_switch_on, post_switch_off
session = req.get_http_session()
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

get_power = create_request_function(config.configuration.url, config.configuration.power_path)
get_imported = create_request_function(config.configuration.url, config.configuration.imported_path)
Expand All @@ -46,14 +43,13 @@ def initializer():
post_switch_on = create_post_function(config.configuration.url, config.configuration.switch_on_path)
post_switch_off = create_post_function(config.configuration.url, config.configuration.switch_off_path)

def update() -> None:
def update() -> ConsumerState:
power = get_power(session)
exported = get_exported(session)
imported = get_imported(session)
exported = get_exported(session) if get_exported is not None else get_default(ConsumerState, "exported")
imported = get_imported(session) if get_imported is not None else get_default(ConsumerState, "imported")

currents = get_currents(session)
temperatures = get_temperatures(session)
if imported is None or exported is None:
imported, exported = sim_counter.sim_count(power)
return ConsumerState(
power=power,
currents=currents,
Expand Down
16 changes: 6 additions & 10 deletions packages/modules/consumers/generic/json/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
from typing import Callable, Optional
import jq

from helpermodules.utils._get_default import get_default
from modules.common import req
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_state import ConsumerState
from modules.common.component_type import ComponentType
from modules.common.configurable_consumer import ConfigurableConsumer, SetLimitData
from modules.common.simcount._simcounter import SimCounterConsumer
from modules.consumers.generic.json.config import Json

log = logging.getLogger(__name__)


def create_consumer(config: Json):
session: Optional[req.CustomSession] = None
sim_counter: Optional[SimCounterConsumer] = None
jq_power = None
jq_imported = None
jq_exported = None
Expand Down Expand Up @@ -45,10 +43,9 @@ def post_function(params: dict):
return post_function

def initializer():
nonlocal session, sim_counter
nonlocal session
nonlocal jq_set_power_limit, jq_switch_on, jq_switch_off
session = req.get_http_session()
sim_counter = SimCounterConsumer(config.id, ComponentType.CONSUMER)

_compile_jq_filters()
jq_set_power_limit = create_post_function(config.configuration.jq_set_power_limit)
Expand All @@ -60,11 +57,10 @@ def update() -> ConsumerState:
power = float(jq_power.input(response).first())
temperatures = float(jq_temperatures.input(response).first()) if jq_temperatures is not None else None
currents = ([float(j.input(response).first()) for j in jq_currents] if jq_currents is not None else None)
if jq_imported is None or jq_exported is None:
imported, exported = sim_counter.sim_count(power)
else:
imported = float(jq_imported.input(response).first())
exported = float(jq_exported.input(response).first())
imported = float(jq_imported.input(response).first()
) if jq_imported is not None else get_default(ConsumerState, "imported")
exported = float(jq_exported.input(response).first()
) if jq_exported is not None else get_default(ConsumerState, "exported")

return ConsumerState(
power=power,
Expand Down
Loading
Loading