import json
import time
from typing import Any, Dict, cast
from typing_extensions import override
from masterpiece.mqtt import MqttMsg
from juham_core import Juham, MasterPieceThread, JuhamThread
from .shelly import Shelly
[docs]
class ShellyMotionSimulatorThread(MasterPieceThread):
"""Thread simulating Shelly Plus 1 wifi relay with four temperature
sensors."""
def __init__(self, topic: str = "", interval: float = 60) -> None:
"""Construct thread for simulating data from Shelly motion sensors.
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__(None)
self.shelly_topic: str = topic
self.interval: float = interval
[docs]
@override
def update_interval(self) -> float:
return self.interval
[docs]
@override
def update(self) -> bool:
super().update()
m = {
"tmp": {"value": 22.5}, # Room temperature value
"sensor": {
"vibration": True, # Vibration status
"motion": False, # Motion status
},
"unixtime": int(time.time()),
}
msg = json.dumps(m)
self.publish(self.shelly_topic, msg, 1, True)
return True
[docs]
class ShellyMotionSimulator(JuhamThread, Shelly):
"""Simulator for Shelly Motion 2 - a wifi motion sensor. Spawns a thread
to generate MQTT messages as if they origin from the actual Shelly motion sensor"""
workerThreadId = ShellyMotionSimulatorThread.get_class_id()
shelly_topic = "shellies/shellymotion2/info"
update_interval = 60
def __init__(
self,
name: str = "shellymotionsensor",
topic: str = "",
interval: float = 60,
) -> None:
"""Create Shelly motion sensor simulator.
Args:
name (str, optional): Name of the object. Defaults to 'shellymotionsensor'.
topic (str, optional): MQTT topic to publish motion sensor events. Defaults to None.
interval (float, optional): interval between events, in seconds. Defaults to None.
"""
super().__init__(name)
self.active_liter_lpm = -1
self.update_ts = None
if topic is not None:
self.topic = topic
if interval is not None:
self.interval = interval
[docs]
@override
def on_connect(self, client: object, userdata: Any, flags: int, rc: int) -> None:
super().on_connect(client, userdata, flags, rc)
if rc == 0:
self.subscribe(self.shelly_topic)
[docs]
@override
def on_message(self, client: object, userdata: Any, msg: MqttMsg) -> None:
if msg.topic == self.shelly_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[str, Any]) -> None:
"""Handle data coming from the Shelly motion sensors.
Simply log the event to indicate the presense of simulated device.
Args:
em (dict): data from the sensor
"""
self.debug(f"Motion sensor sensor {em}")
[docs]
@override
def run(self) -> None:
self.worker = cast(
ShellyMotionSimulatorThread,
Juham.instantiate(ShellyMotionSimulatorThread.get_class_id()),
)
self.worker.shelly_topic = self.shelly_topic
self.worker.interval = self.update_interval
super().run()
[docs]
@override
def to_dict(self) -> Dict[str, Any]:
data: Dict[str, Any] = super().to_dict()
data["_shellymotionsimulator"] = {"shelly_topic": self.shelly_topic}
return data
[docs]
@override
def from_dict(self, data: Dict[str, Any]) -> None:
super().from_dict(data)
if "_shellymotionsimulator" in data:
for key, value in data["_shellymotionsimulator"].items():
setattr(self, key, value)