pqopen-lib 0.7.2__tar.gz → 0.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/PKG-INFO +1 -1
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/powersystem.py +2 -2
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/storagecontroller.py +55 -1
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pyproject.toml +1 -1
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/powersystem-test.py +31 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/.gitignore +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/LICENSE +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/README.md +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/__init__.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/eventdetector.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/helper.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/powerquality.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/pqopen/zcd.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/data_files/event_data_level_low.csv +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/eventdetector-test.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/helper-test.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/powerquality-test.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/storagecontroller-test.py +0 -0
- {pqopen_lib-0.7.2 → pqopen_lib-0.7.4}/test/zcd-test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pqopen-lib
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: A power quality processing library for calculating parameters from waveform data
|
|
5
5
|
Project-URL: Homepage, https://github.com/DaqOpen/pqopen-lib
|
|
6
6
|
Project-URL: Issues, https://github.com/DaqOpen/pqopen-lib/issues
|
|
@@ -493,13 +493,13 @@ class PowerSystem(object):
|
|
|
493
493
|
if "w_pos" in self._calc_channels["multi_period"]["energy"]:
|
|
494
494
|
prev_w_pos_value = self._calc_channels["multi_period"]["energy"]["w_pos"].last_sample_value
|
|
495
495
|
energy = (stop_sidx - start_sidx)/self._samplerate/3600*p_sum if p_sum > 0 else 0.0 # Energy in Wh
|
|
496
|
-
self._calc_channels["multi_period"]["energy"]["w_pos"].put_data_single(stop_sidx, prev_w_pos_value + energy)
|
|
496
|
+
self._calc_channels["multi_period"]["energy"]["w_pos"].put_data_single(stop_sidx, prev_w_pos_value + float(energy))
|
|
497
497
|
|
|
498
498
|
# Calculate Negative Energy
|
|
499
499
|
if "w_neg" in self._calc_channels["multi_period"]["energy"]:
|
|
500
500
|
prev_w_neg_value = self._calc_channels["multi_period"]["energy"]["w_neg"].last_sample_value
|
|
501
501
|
energy = -(stop_sidx - start_sidx)/self._samplerate/3600*p_sum if p_sum < 0 else 0.0 # Energy in Wh
|
|
502
|
-
self._calc_channels["multi_period"]["energy"]["w_neg"].put_data_single(stop_sidx, prev_w_neg_value + energy)
|
|
502
|
+
self._calc_channels["multi_period"]["energy"]["w_neg"].put_data_single(stop_sidx, prev_w_neg_value + float(energy))
|
|
503
503
|
|
|
504
504
|
# Calculate unbalance (3-phase)
|
|
505
505
|
if "unbal_0" in self._calc_channels["multi_period"]["voltage"]:
|
|
@@ -342,6 +342,14 @@ class StorageController(object):
|
|
|
342
342
|
topic_prefix=ep_config.get("topic_prefix", "pqopen/data"),
|
|
343
343
|
qos=ep_config.get("qos", 0))
|
|
344
344
|
self._configured_eps["ha_mqtt"] = ha_mqtt_storage_endpoint
|
|
345
|
+
elif ep_type == "victron_mqtt":
|
|
346
|
+
victron_mqtt_storage_endpoint = VictronStorageEndpoint(name="victron_mqtt",
|
|
347
|
+
device_id=device_id,
|
|
348
|
+
mqtt_host=ep_config.get("hostname", "localhost"),
|
|
349
|
+
client_id=ep_config.get("client_id", "pqopen-victron"),
|
|
350
|
+
topic_prefix=ep_config.get("topic_prefix", "pqopen/meter"),
|
|
351
|
+
qos=ep_config.get("qos", 0))
|
|
352
|
+
self._configured_eps["victron_mqtt"] = victron_mqtt_storage_endpoint
|
|
345
353
|
else:
|
|
346
354
|
raise NotImplementedError(f"{ep_type:s} not implemented")
|
|
347
355
|
for sp_name, sp_config in storage_plans.items():
|
|
@@ -572,7 +580,53 @@ class HomeAssistantStorageEndpoint(StorageEndpoint):
|
|
|
572
580
|
self._client.publish(self._topic_prefix, json.dumps(data).encode("utf-8"), qos=self._qos)
|
|
573
581
|
logger.debug("Published HA-MQTT Message")
|
|
574
582
|
|
|
575
|
-
|
|
583
|
+
class VictronStorageEndpoint(StorageEndpoint):
|
|
584
|
+
"""Represents a MQTT endpoint (MQTT) for Victron Power Meter Emulation.
|
|
585
|
+
https://github.com/mr-manuel/venus-os_dbus-mqtt-grid
|
|
586
|
+
"""
|
|
587
|
+
def __init__(self,
|
|
588
|
+
name: str,
|
|
589
|
+
device_id: str,
|
|
590
|
+
mqtt_host: str,
|
|
591
|
+
client_id: str,
|
|
592
|
+
topic_prefix: str = "pqopen/victron",
|
|
593
|
+
qos: int = 0):
|
|
594
|
+
""" Create an endpoint for Victron via MQTT
|
|
595
|
+
|
|
596
|
+
Parameters:
|
|
597
|
+
name: The name of the endpoint
|
|
598
|
+
device_id: The device Id
|
|
599
|
+
mqtt_host: hostname of the MQTT broker.
|
|
600
|
+
client_id: name to be used for mqtt client identification
|
|
601
|
+
topic_prefix: topic prefix before device-id, no trailing /
|
|
602
|
+
qos: mqtt quality of service. valid values 0, 1, 2
|
|
603
|
+
"""
|
|
604
|
+
super().__init__(name, measurement_id=None)
|
|
605
|
+
self._device_id = device_id
|
|
606
|
+
self._topic_prefix = topic_prefix
|
|
607
|
+
self._qos = qos
|
|
608
|
+
self._client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=client_id, clean_session=True)
|
|
609
|
+
self._client.on_connect = self._on_connect
|
|
610
|
+
self._client.connect_async(host=mqtt_host)
|
|
611
|
+
self._client.loop_start()
|
|
612
|
+
|
|
613
|
+
def _on_connect(self, client, userdata, flags, reason_code, properties):
|
|
614
|
+
logger.debug("Victron-MQTT Connected")
|
|
615
|
+
|
|
616
|
+
def write_aggregated_data(self, data: dict, timestamp_us: int, interval_seconds: int, **kwargs):
|
|
617
|
+
""" Write an aggregated data message
|
|
618
|
+
|
|
619
|
+
Parameters:
|
|
620
|
+
data: The data object to be sent
|
|
621
|
+
timestamp_us: Timestamp (in µs) of the data set
|
|
622
|
+
interval_seconds: Aggregation intervall, used as data tag
|
|
623
|
+
"""
|
|
624
|
+
if "P" in data:
|
|
625
|
+
payload = {"grid": {"power": data["P"]}}
|
|
626
|
+
self._client.publish(self._topic_prefix, json.dumps(payload).encode("utf-8"), qos=self._qos)
|
|
627
|
+
logger.debug("Published Victron MQTT Message")
|
|
628
|
+
else:
|
|
629
|
+
logger.debug("Can't publish Victron MQTT Message, P not found")
|
|
576
630
|
|
|
577
631
|
class CsvStorageEndpoint(StorageEndpoint):
|
|
578
632
|
"""Represents a csv storage endpoint"""
|
|
@@ -451,6 +451,37 @@ class TestPowerSystemCalculationThreePhase(unittest.TestCase):
|
|
|
451
451
|
p_neg, sidx = self.power_system.output_channels["P_neg"].read_data_by_acq_sidx(0, u1_values.size)
|
|
452
452
|
self.assertIsNone(np.testing.assert_allclose(p_neg, expected_p_neg, rtol=0.01))
|
|
453
453
|
|
|
454
|
+
def test_energy_calc_pos_highoffset(self):
|
|
455
|
+
t = np.linspace(0, 1, int(self.power_system._samplerate), endpoint=False)
|
|
456
|
+
u1_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t)
|
|
457
|
+
u2_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t - 120*np.pi/180)
|
|
458
|
+
u3_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t + 120*np.pi/180)
|
|
459
|
+
i1_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t)
|
|
460
|
+
i2_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t -120*np.pi/180)
|
|
461
|
+
i3_values = 1.0*np.sqrt(2)*np.sin(2*np.pi*50*t+ 120*np.pi/180)
|
|
462
|
+
|
|
463
|
+
Path(SCRIPT_DIR+"/data_files/energy.json").write_text(json.dumps({"W_pos": 1_000_000.1, "W_neg": 10}))
|
|
464
|
+
|
|
465
|
+
expected_w_pos = np.array([1, 2, 3, 4])*3*0.2/3600 + 1_000_000.1
|
|
466
|
+
expected_w_neg = np.array([1, 2, 3, 4])*0.0/3600 + 10
|
|
467
|
+
|
|
468
|
+
self.power_system.enable_energy_channels(Path(SCRIPT_DIR+"/data_files/energy.json"))
|
|
469
|
+
self.u1_channel.put_data(u1_values)
|
|
470
|
+
self.u2_channel.put_data(u2_values)
|
|
471
|
+
self.u3_channel.put_data(u3_values)
|
|
472
|
+
|
|
473
|
+
self.i1_channel.put_data(i1_values)
|
|
474
|
+
self.i2_channel.put_data(i2_values)
|
|
475
|
+
self.i3_channel.put_data(i3_values)
|
|
476
|
+
|
|
477
|
+
self.power_system.process()
|
|
478
|
+
|
|
479
|
+
# Check Energy W_pos
|
|
480
|
+
w_pos, sidx = self.power_system.output_channels["W_pos"].read_data_by_acq_sidx(0, u1_values.size)
|
|
481
|
+
self.assertIsNone(np.testing.assert_array_almost_equal(w_pos, expected_w_pos))
|
|
482
|
+
w_neg, sidx = self.power_system.output_channels["W_neg"].read_data_by_acq_sidx(0, u1_values.size)
|
|
483
|
+
self.assertIsNone(np.testing.assert_array_almost_equal(w_neg, expected_w_neg))
|
|
484
|
+
|
|
454
485
|
class TestPowerSystemNperSync(unittest.TestCase):
|
|
455
486
|
def setUp(self):
|
|
456
487
|
self.u_channel = AcqBuffer()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|