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 data/config/mosquitto/openwb_local.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# openwb-version:17
# openwb-version:18
listener 1886 localhost
allow_anonymous true

Expand Down Expand Up @@ -31,6 +31,8 @@ topic openWB/internal_chargepoint/# out 2
topic openWB/io/# out 2
topic openWB/internal_io/# out 2

topic openWB/mqtt/# both 2

topic openWB/pv/config/configured out 2
topic openWB/pv/get/# out 2
topic openWB/pv/+/config/# out 2
Expand Down
24 changes: 21 additions & 3 deletions packages/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from control.pv import Pv, PvData
from control.pv import Get as PvGet
from helpermodules import hardware_configuration, pub, timecheck
from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule
from modules.common.component_state import ChargepointState
from modules.common.store._api import LoggingValueStore


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -118,19 +121,34 @@ def data_() -> None:
get=Mock(spec=Get, currents=[30, 0, 0], power=6900,
daily_imported=10000, daily_exported=0, imported=56000,
fault_state=0),
set=Mock(spec=Set, loadmanagement_available=True))),
set=Mock(spec=Set, loadmanagement_available=True)),
chargepoint_module=Mock(spec=ChargepointModule,
store=Mock(spec=LoggingValueStore,
delegate=Mock(spec=LoggingValueStore,
state=ChargepointState(currents=[30, 0, 0],
power=6900))))),
"cp4": Mock(spec=Chargepoint, data=Mock(spec=ChargepointData,
config=Mock(spec=Config, phase_1=2),
get=Mock(spec=Get, currents=[0, 15, 15], power=6900,
daily_imported=10000, daily_exported=0, imported=60000,
fault_state=0),
set=Mock(spec=Set, loadmanagement_available=True))),
set=Mock(spec=Set, loadmanagement_available=True)),
chargepoint_module=Mock(spec=ChargepointModule,
store=Mock(spec=LoggingValueStore,
delegate=Mock(spec=LoggingValueStore,
state=ChargepointState(currents=[0, 15, 15],
power=6900))))),
"cp5": Mock(spec=Chargepoint, data=Mock(spec=ChargepointData,
config=Mock(spec=Config, phase_1=3),
get=Mock(spec=Get, currents=[10]*3, power=6900,
daily_imported=10000, daily_exported=0, imported=62000,
fault_state=0),
set=Mock(spec=Set, loadmanagement_available=True)))}
set=Mock(spec=Set, loadmanagement_available=True)),
chargepoint_module=Mock(spec=ChargepointModule,
store=Mock(spec=LoggingValueStore,
delegate=Mock(spec=LoggingValueStore,
state=ChargepointState(currents=[10]*3,
power=6900)))))}
data.data.bat_data.update({"bat2": Mock(spec=Bat, num=2, data=Mock(spec=BatData, get=Mock(
spec=BatGet, power=-5000, daily_imported=7000, daily_exported=3000, imported=12000, exported=10000,
currents=None, fault_state=0),
Expand Down
2 changes: 1 addition & 1 deletion packages/control/bat_all_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def test_get_power_limit(params: PowerLimitParams, data_, monkeypatch):
monkeypatch.setattr(bat_all, "get_chargepoints_by_chargemodes", get_chargepoints_by_chargemodes_mock)
get_evu_counter_mock = Mock(return_value=data.data.counter_data["counter0"])
monkeypatch.setattr(data.data.counter_all_data, "get_evu_counter", get_evu_counter_mock)
get_controllable_bat_components_mock = Mock(return_value=[MqttBat(MqttBatSetup(id=2))])
get_controllable_bat_components_mock = Mock(return_value=[MqttBat(MqttBatSetup(id=2), device_id=0)])
monkeypatch.setattr(bat_all, "get_controllable_bat_components", get_controllable_bat_components_mock)

data.data.bat_all_data.get_power_limit()
Expand Down
38 changes: 36 additions & 2 deletions packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@


class UpdateConfig:
DATASTORE_VERSION = 77

DATASTORE_VERSION = 79

valid_topic = [
"^openWB/bat/config/configured$",
"^openWB/bat/config/power_limit_mode$",
Expand Down Expand Up @@ -254,6 +256,23 @@ class UpdateConfig:
"^openWB/io/action/[0-9]+/config$",
"^openWB/io/action/[0-9]+/timestamp$",

"^openWB/mqtt/bat/[0-9]+/get/power$",
"^openWB/mqtt/bat/[0-9]+/get/soc$",
"^openWB/mqtt/bat/[0-9]+/get/imported$",
"^openWB/mqtt/bat/[0-9]+/get/exported$",
"^openWB/mqtt/counter/[0-9]+/get/currents$",
"^openWB/mqtt/counter/[0-9]+/get/imported$",
"^openWB/mqtt/counter/[0-9]+/get/exported$",
"^openWB/mqtt/counter/[0-9]+/get/power$",
"^openWB/mqtt/counter/[0-9]+/get/frequency$",
"^openWB/mqtt/counter/[0-9]+/get/power_factors$",
"^openWB/mqtt/counter/[0-9]+/get/powers$",
"^openWB/mqtt/counter/[0-9]+/get/voltages$",
"^openWB/mqtt/inverter/[0-9]+/get/currents$",
"^openWB/mqtt/inverter/[0-9]+/get/power$",
"^openWB/mqtt/inverter/[0-9]+/get/exported$",
"^openWB/mqtt/inverter/[0-9]+/get/dc_power$",

"^openWB/set/log/request",
"^openWB/set/log/data",

Expand Down Expand Up @@ -489,7 +508,7 @@ class UpdateConfig:
("openWB/general/chargemode_config/phase_switch_delay", 7),
("openWB/general/chargemode_config/pv_charging/phases_to_use", 0),
("openWB/general/chargemode_config/retry_failed_phase_switches",
ChargemodeConfig().retry_failed_phase_switches),
ChargemodeConfig().retry_failed_phase_switches),
("openWB/general/chargemode_config/scheduled_charging/phases_to_use", 0),
("openWB/general/chargemode_config/scheduled_charging/phases_to_use_pv", 0),
("openWB/general/chargemode_config/time_charging/phases_to_use", 1),
Expand Down Expand Up @@ -2048,3 +2067,18 @@ def upgrade(topic: str, payload) -> Optional[dict]:
return {topic: configuration_payload}
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 78)

def upgrade_datastore_78(self) -> None:
def upgrade(topic: str, payload) -> Optional[dict]:
for topic, payload in self.all_received_topics.items():
if (re.search("openWB/system/device/[0-9]+", topic) is not None or
re.search("openWB/chargepoint/[0-9]+/config", topic) is not None):
payload = decode_payload(payload)
if payload.get("type") == "mqtt":
pub_system_message(
{}, "Die Topics für MQTT-Komponenten und MQTT-Ladepunkte wurden angepasst. Bitte "
"aktualisiere die Topics in Deinen angebundenen (Smarthome-)Systemen.", MessageType.WARNING)
# Nachricht nur einmal senden
break
self._loop_all_received_topics(upgrade)
self.__update_topic("openWB/system/datastore_version", 79)
56 changes: 47 additions & 9 deletions packages/modules/chargepoints/mqtt/chargepoint_module.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,71 @@
import logging

from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext
from helpermodules.broker import BrokerClient
from helpermodules.pub import Pub
from helpermodules.utils.topic_parser import decode_payload
from modules.chargepoints.mqtt.config import Mqtt
from modules.common.abstract_chargepoint import AbstractChargepoint
from modules.common.abstract_device import DeviceDescriptor
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.component_state import ChargepointState
from modules.common.fault_state import ComponentInfo, FaultState
from modules.common.store._chargepoint import get_chargepoint_value_store


log = logging.getLogger(__name__)


class ChargepointModule(AbstractChargepoint):
def __init__(self, config: Mqtt) -> None:
self.config = config
self.fault_state = FaultState(ComponentInfo(
self.config.id,
"Ladepunkt", "chargepoint"))
self.client_error_context = ErrorTimerContext(
f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True)
self.store = get_chargepoint_value_store(self.config.id)
self.fault_state = FaultState(ComponentInfo(self.config.id, "Ladepunkt", "chargepoint"))

def set_current(self, current: float) -> None:
log.debug("MQTT-Ladepunkte abonnieren die Soll-Stromstärke direkt vom Broker.")
Pub().pub(f"openWB/mqtt/chargepoint/{self.config.id}/set/current", current)

def get_values(self) -> None:
with SingleComponentUpdateContext(self.fault_state):
log.debug("MQTT-Ladepunkte müssen nicht ausgelesen werden.")
def on_connect(client, userdata, flags, rc):
client.subscribe(f"openWB/mqtt/chargepoint/{self.config.id}/#")

def on_message(client, userdata, message):
received_topics.update({message.topic: decode_payload(message.payload)})

received_topics = {}
BrokerClient(f"subscribeMqttChargepoint{self.config.id}",
on_connect, on_message).start_finite_loop()

if received_topics:
log.debug(f"Empfange MQTT Daten für Ladepunkt {self.config.id}: {received_topics}")
topic_prefix = f"openWB/mqtt/chargepoint/{self.config.id}/get/"
chargepoint_state = ChargepointState(
power=received_topics.get(f"{topic_prefix}power"),
phases_in_use=received_topics.get(f"{topic_prefix}phases_in_use"),
imported=received_topics.get(f"{topic_prefix}imported"),
exported=received_topics.get(f"{topic_prefix}exported"),
serial_number=received_topics.get(f"{topic_prefix}serial_number"),
powers=received_topics.get(f"{topic_prefix}powers"),
voltages=received_topics.get(f"{topic_prefix}voltages"),
currents=received_topics.get(f"{topic_prefix}currents"),
power_factors=received_topics.get(f"{topic_prefix}power_factors"),
plug_state=received_topics.get(f"{topic_prefix}plug_state"),
charge_state=received_topics.get(f"{topic_prefix}charge_state"),
rfid=received_topics.get(f"{topic_prefix}rfid"),
rfid_timestamp=received_topics.get(f"{topic_prefix}rfid_timestamp"),
frequency=received_topics.get(f"{topic_prefix}frequency"),
soc=received_topics.get(f"{topic_prefix}soc"),
soc_timestamp=received_topics.get(f"{topic_prefix}soc_timestamp"),
vehicle_id=received_topics.get(f"{topic_prefix}vehicle_id"),
evse_current=received_topics.get(f"{topic_prefix}evse_current"),
max_evse_current=received_topics.get(f"{topic_prefix}max_evse_current")
)
self.store.set(chargepoint_state)
else:
raise Exception(f"Keine MQTT Daten für Gerät {self.config.id} empfangen")

def switch_phases(self, phases_to_use: int, duration: int) -> None:
log.warning("Phasenumschaltung für MQTT-Ladepunkte nicht unterstützt.")
Pub().pub(f"openWB/mqtt/chargepoint/{self.config.id}/set/phases_to_use", phases_to_use)


chargepoint_descriptor = DeviceDescriptor(configuration_factory=Mqtt)
66 changes: 36 additions & 30 deletions packages/modules/common/store/_counter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from operator import add

from control import data
Expand All @@ -10,6 +11,9 @@
from modules.common.store._api import LoggingValueStore
from modules.common.store._broker import pub_to_broker
from modules.common.store.ramdisk import files
from modules.common.utils.component_parser import get_component_obj_by_id

log = logging.getLogger(__name__)


class CounterValueStoreRamdisk(ValueStore[CounterState]):
Expand Down Expand Up @@ -70,50 +74,52 @@ def calc_virtual(self, state: CounterState) -> CounterState:
self.incomplete_currents = False

def add_current_power(element):
if element.data.get.currents is not None:
if sum(element.data.get.currents) == 0 and element.data.get.power != 0:
if hasattr(element, "currents") and element.currents is not None:
if sum(element.currents) == 0 and element.power != 0:
self.currents = [0, 0, 0]
self.incomplete_currents = True
else:
self.currents = list(map(add, self.currents, element.data.get.currents))
self.currents = list(map(add, self.currents, element.currents))
else:
self.currents = [0, 0, 0]
self.incomplete_currents = True
self.power += element.data.get.power
self.power += element.power

def add_imported_exported(element):
self.imported += element.data.get.imported
self.exported += element.data.get.exported
self.imported += element.imported
self.exported += element.exported

def add_exported(element):
self.exported += element.data.get.exported
self.exported += element.exported

counter_all = data.data.counter_all_data
elements = counter_all.get_elements_for_downstream_calculation(self.delegate.delegate.num)
for element in elements:
if element["type"] == ComponentType.CHARGEPOINT.value:
chargepoint = data.data.cp_data[f"cp{element['id']}"]
try:
self.currents = list(map(add,
self.currents,
convert_cp_currents_to_evu_currents(
chargepoint.data.config.phase_1,
chargepoint.data.get.currents)))
except KeyError:
raise KeyError("Für den virtuellen Zähler muss der Anschluss der Phasen von Ladepunkt"
f" {chargepoint.data.config.name} an die Phasen des EVU Zählers "
"angegeben werden.")
self.power += chargepoint.data.get.power
self.imported += chargepoint.data.get.imported
elif element["type"] == ComponentType.BAT.value:
add_current_power(data.data.bat_data[f"bat{element['id']}"])
add_imported_exported(data.data.bat_data[f"bat{element['id']}"])
elif element["type"] == ComponentType.COUNTER.value:
add_current_power(data.data.counter_data[f"counter{element['id']}"])
add_imported_exported(data.data.counter_data[f"counter{element['id']}"])
elif element["type"] == ComponentType.INVERTER.value:
add_current_power(data.data.pv_data[f"pv{element['id']}"])
add_exported(data.data.pv_data[f"pv{element['id']}"])
try:
if element["type"] == ComponentType.CHARGEPOINT.value:
chargepoint = data.data.cp_data[f"cp{element['id']}"]
chargepoint_state = chargepoint.chargepoint_module.store.delegate.state
try:
self.currents = list(map(add,
self.currents,
convert_cp_currents_to_evu_currents(
chargepoint.data.config.phase_1,
chargepoint_state.currents)))
except KeyError:
raise KeyError("Für den virtuellen Zähler muss der Anschluss der Phasen von Ladepunkt"
f" {chargepoint.data.config.name} an die Phasen des EVU Zählers "
"angegeben werden.")
self.power += chargepoint_state.power
self.imported += chargepoint_state.imported
else:
component = get_component_obj_by_id(element['id'])
add_current_power(component.store.delegate.state)
if element["type"] == ComponentType.INVERTER.value:
add_exported(component.store.delegate.state)
else:
add_imported_exported(component.store.delegate.state)
except Exception:
log.exception(f"Fehler beim Hinzufügen der Werte für Element {element}")

if self.incomplete_currents:
self.currents = None
Expand Down
Loading