Skip to content

Commit 5e6d679

Browse files
committed
added electric meter
1 parent 2e0882f commit 5e6d679

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import random
2+
import datetime
3+
import uuid
4+
import time
5+
import logging
6+
import paho.mqtt.client as mqtt
7+
from kaa_client import KaaClient, CommandsDto, ConfigurationStatusResponseDto
8+
from paho.mqtt.client import MQTTMessage
9+
10+
class ElectricMeter(object):
11+
def __init__(self, client: KaaClient):
12+
self.kaa_client = client
13+
self.lines = ["l1", "l2", "l3"]
14+
self.time = time.time()
15+
self.l1 = 1
16+
self.l2 = 1
17+
self.l3 = 1
18+
self.consumed_power = random.randint(991, 110000)
19+
self.config = {
20+
"overdriveThreshold": 240,
21+
"powerThreshold": None
22+
}
23+
self.simulateOverdrive = False
24+
25+
def toggle(self, status: bool) -> None:
26+
self.l1 = status
27+
self.l2 = status
28+
self.l3 = status
29+
30+
@staticmethod
31+
def get_device_metadata():
32+
return {
33+
"model": "Qubino Smart Meter 3-Phase",
34+
"lon": "-0.0395317",
35+
"lat": "51.5025671",
36+
"lastMaintenance": f"{datetime.datetime.now()}",
37+
"type": "electric meter",
38+
"serial": f"{uuid.uuid4()}",
39+
"Address": "Dock Hill Ave, Rotherhithe, London SE16 6AX, United Kingdom"
40+
}
41+
42+
def _is_overdrive(self, voltage_total: int) -> bool:
43+
return bool(self.config["overdriveThreshold"] and voltage_total > self.config["overdriveThreshold"] * 3)
44+
45+
def get_voltage(self):
46+
if self.simulateOverdrive is True:
47+
return random.randint(251, 300)
48+
else:
49+
return random.randint(215, 230)
50+
51+
def get_current(self, line):
52+
if getattr(self, line):
53+
return random.randint(8, 10) * getattr(self, line)
54+
else:
55+
return 0
56+
57+
@staticmethod
58+
def get_power(voltage, current):
59+
return voltage*current
60+
61+
@staticmethod
62+
def get_current_reactive(current_active):
63+
return current_active / (random.randint(1, 10))
64+
65+
def get_consumed_power(self, total_power):
66+
time_delta = (time.time() - self.time) / 3600
67+
self.consumed_power = self.consumed_power + time_delta * total_power
68+
return self.consumed_power
69+
70+
def get_power_status(self):
71+
if self.l1 and self.l2 and self.l3:
72+
return 1
73+
return 0
74+
75+
"""
76+
Generates telemetry data sample.
77+
"""
78+
def get_data_sample(self):
79+
data = {}
80+
for line in self.lines:
81+
data[f"voltage_{line}"] = self.get_voltage()
82+
voltage_total = sum([data[f"voltage_{line}"] for line in self.lines])
83+
if self._is_overdrive(voltage_total):
84+
for line in self.lines:
85+
setattr(self, line, 0)
86+
data[f"status_{line}"] = 0
87+
for line in self.lines:
88+
data[f"current_{line}_active"] = self.get_current(line)
89+
data[f"current_{line}_reactive"] = self.get_current_reactive(data[f"current_{line}_active"])
90+
data[f"current_{line}_total"] = data[f"current_{line}_active"] + data[f"current_{line}_reactive"]
91+
data[f"power_{line}_active"] = self.get_power(data[f"voltage_{line}"], data[f"current_{line}_active"])
92+
data[f"power_{line}_reactive"] = self.get_power(data[f"voltage_{line}"], data[f"current_{line}_reactive"])
93+
data[f"power_{line}_total"] = self.get_power(data[f"voltage_{line}"], data[f"current_{line}_total"])
94+
data[f"status_{line}"] = getattr(self, line)
95+
96+
data["current_total"] = sum([data[f"current_{line}_total"] for line in self.lines])
97+
data["power_total"] = sum([data[f"power_{line}_total"] for line in self.lines])
98+
data["power_active"] = sum([data[f"power_{line}_active"] for line in self.lines])
99+
data["power_reactive"] = sum([data[f"power_{line}_reactive"] for line in self.lines])
100+
data["consumed_power"] = self.get_consumed_power(data["power_total"])
101+
data["power_status"] = self.get_power_status()
102+
data["voltage_total"] = voltage_total
103+
104+
return data
105+
106+
"""
107+
command type: electric_meter_toggle
108+
example of payloads:
109+
Turn off all lines
110+
{
111+
"l1": 0,
112+
"l2": 0,
113+
"l3": 0
114+
}
115+
Turn on the Line 1
116+
{
117+
"l1": 1
118+
}
119+
"""
120+
def electric_meter_handler(self, client: mqtt.Client, userdata, message: MQTTMessage):
121+
command_payload = str(message.payload.decode('utf-8'))
122+
logging.info(f"Received command: [{message.topic}] []")
123+
response_topic = KaaClient.get_command_response_topic(message, "electric_meter_toggle")
124+
commands = CommandsDto(command_payload).get_command_responses()
125+
for command in commands:
126+
try:
127+
for key in command.payload.keys():
128+
setattr(self, key, int(command.payload[key]))
129+
command.status_code = 200
130+
command.payload[key] = "status changed"
131+
except Exception as error:
132+
logging.error(f"Invalid command payload [{error}] [{command.to_dict()}]")
133+
client.publish(response_topic, self.kaa_client.compose_commands_result(commands))
134+
135+
136+
"""
137+
Command to simulate overdrive
138+
Send command type `electric_meter_overdrive` to turn on the overdrive.
139+
Send payload { "off": true } to switch to normal mode.
140+
"""
141+
def electric_meter_overdrive_handler(self, client: mqtt.Client, userdata, message: MQTTMessage):
142+
command_payload = str(message.payload.decode('utf-8'))
143+
logging.info(f"Received command: [{message.topic}] []")
144+
response_topic = KaaClient.get_command_response_topic(message, "electric_meter_overdrive")
145+
commands = CommandsDto(command_payload).get_command_responses()
146+
last_command = commands[-1].to_dict()
147+
for command in commands:
148+
command.status_code = 200
149+
command.reasonPhrase = "Ok"
150+
if last_command and "off" in last_command["payload"]:
151+
self.simulateOverdrive = False
152+
else:
153+
self.simulateOverdrive = True
154+
client.publish(response_topic, self.kaa_client.compose_commands_result(commands))
155+
156+
"""
157+
Handle value `overdriveThreshold` published from configuration.
158+
When avarage voltage is less then threshold value simulator will turn off all the lines
159+
"""
160+
def configuration_handler(self, client: mqtt.Client, userdata, message):
161+
configuration_payload = str(message.payload.decode('utf-8'))
162+
logging.info("Received configuration")
163+
if configuration_payload:
164+
self.config = ConfigurationStatusResponseDto(configuration_payload).to_dict()["config"]
165+

0 commit comments

Comments
 (0)