Source code for juham.simulation.energymeter_simulator

import json
from juham.base import Base
from juham.base.time import timestamp
from juham.web import IWorkerThread, RThread


class EnergyMeterSimulatorThread(IWorkerThread):
    """Thread simulating Energy Meter."""

    _power: float = 1000.0  # W
    _power_topic: str = "power"
    _interval: float = 10  # 10 seconds

    def __init__(self) -> None:
        """Construct a thread for publishing power data.

        Args:
            topic (str, optional): MQTT topic to post the sensor readings. Defaults to None.
            interval (float, optional): Interval specifying how often the sensor is read. Defaults to 60 seconds.
        """
        super().__init__()
        self.current_ts: float = timestamp()

    @classmethod
    def initialize(cls, power_topic: str, power: float, interval: float):
        """Initialize thread  class attributes.

        Args:
            power_topic (str): topic to publish the energy meter readings
            power (float): power  to be simulated, the default is 1kW
            interval (float): update interval, the default is 10s
        """
        cls._power = power
        cls._interval = interval
        cls._power_topic = power_topic

    # @override
    def update_interval(self) -> float:
        return self._interval

    def publish_active_power(self, ts):
        """Publish the active power, also known as real power. This is that
        part of the  power that can be converted to useful work.

        Args:
            ts (str): time stamp of the event

        """
        dt = ts - self.current_ts
        self.current_ts = ts

        msg = {
            "timestamp": ts,
            "real_a": self._power * dt,
            "real_b": self._power * dt,
            "real_c": self._power * dt,
            "real_total": 3 * self._power * dt,
        }
        self.publish(self._power_topic, json.dumps(msg), 1, True)

    def update(self) -> bool:
        super().update()
        self.publish_active_power(timestamp())
        return True


[docs] class EnergyMeterSimulator(RThread): """Simulator energy meter sensor. Spawns a thread to simulate Shelly PM mqtt messages""" workerThreadId = EnergyMeterSimulatorThread.get_class_id() power_topic = Base.mqtt_root_topic + "/powerconsumption" # target topic update_interval: float = 10 power: float = 1000.0 def __init__( self, name="em", interval: float = 0, ) -> None: """Create energy meter simulator. Args: name (str, optional): Name of the object. Defaults to 'em'. topic (str, optional): MQTT topic to publish the energy meter reports. Defaults to None. interval (float, optional): interval between events, in seconds. Defaults to None. """ super().__init__(name) self.update_ts: float = 0.0 if interval > 0.0: self.update_interval = interval # @override
[docs] def on_message(self, client, userdata, msg): if msg.topic == self.power_topic: em = json.loads(msg.payload.decode()) self.on_sensor(em) else: super().on_message(client, userdata, msg)
[docs] def on_sensor(self, em: dict) -> None: """Handle data coming from the energy meter. Simply log the event to indicate the presense of simulated device. Args: em (dict): data from the sensor """ self.debug(f"Simulated power meter sensor {em}")
[docs] def run(self): EnergyMeterSimulatorThread.initialize( self.power_topic, self.update_interval, self.power ) self.worker = Base.instantiate(EnergyMeterSimulatorThread.get_class_id()) super().run()
[docs] def to_dict(self): data = super().to_dict() data["_shellypm"] = {"power_topic": self.power_topic} return data
[docs] def from_dict(self, data): super().from_dict(data) if "_shellypm" in data: for key, value in data["_shellypm"].items(): setattr(self, key, value)