def __init__(self): self.serial_reader = SerialReader( device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V5, telegram_specification=telegram_specifications.V4 ) self.general = 'P1_log.csv' self.minute = 'P1_log.minute' self.hour = 'P1_log.hour' self.day = 'P1_log.day' self.verbose, self.logging = False, False self.powerhist = [] self.lastlog = {self.general: 0, self.minute: 0, self.hour: 0, self.day: 0} try: arg = sys.argv[1] if arg == '-v': self.verbose = True elif arg == '-l': self.logging = True except IndexError: pass
def main(host, topic, device): mqtt = paho.Client("pirate") while True: try: serial_reader = SerialReader( device=device, serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4) print("Waiting for P1 port measurement..") for telegram in serial_reader.read(): measurement = {} for key, value in telegram.items(): if hasattr(value, "value") and ( isinstance(value.value, int) or isinstance(value.value, decimal.Decimal)): name = determine_obis_name(key) measurement[name] = float(value.value) for key, value in measurement.items(): # Need to connect each time, or for some reason it disconnects without Exception mqtt.connect(host=host, port=32184) mqtt.publish(topic=topic + "/" + key, payload=value) mqtt.disconnect() except Exception as e: print(str(e)) print("Pausing and restarting...") time.sleep(10)
def handle(self, *args, **options): serial_reader = SerialReader( device=options['device'], serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4) for telegram in serial_reader.read(): message_datetime = telegram[P1_MESSAGE_TIMESTAMP] tariff = telegram[ELECTRICITY_ACTIVE_TARIFF] tariff = int(tariff.value) electricity_used_total \ = telegram[ELECTRICITY_USED_TARIFF_ALL[tariff - 1]] electricity_delivered_total = \ telegram[ELECTRICITY_DELIVERED_TARIFF_ALL[tariff - 1]] electricity_used_actual = \ telegram[CURRENT_ELECTRICITY_USAGE] electricity_delivered_actual = \ telegram[CURRENT_ELECTRICITY_DELIVERY] gas_metric = telegram[HOURLY_GAS_METER_READING] electricity_used_tariff = \ Meter.manager.electricity_used_tariff(tariff) electricity_delivered_tariff = \ Meter.manager.electricity_delivered_tariff(tariff) gas_tariff = Meter.manager.gas_tariff() Meter.manager.electricity_used().metrics_actual.create( datetime=message_datetime.value, value=electricity_used_actual.value) if _is_new_hourly_metric(electricity_used_tariff, message_datetime.value): electricity_used_tariff.metrics_total.create( datetime=message_datetime.value, value_total=electricity_used_total.value) if electricity_delivered_total.value: Meter.manager.electricity_delivered().metrics_actual.create( datetime=message_datetime.value, value=electricity_delivered_actual.value) if _is_new_hourly_metric(electricity_delivered_tariff, message_datetime.value): electricity_delivered_tariff.metrics_total.create( datetime=message_datetime.value, value_total=electricity_delivered_total.value) if _is_new_hourly_metric(gas_tariff, gas_metric.datetime): gas_tariff.metrics_total.create(value_total=gas_metric.value, datetime=gas_metric.datetime)
def init_reader(self): serial_reader = SerialReader( device="/dev/ttyUSB0", serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4) return serial_reader
def setUp(self): device_port = 'tty_socat_device' client_port = 'tty_socat_client' self.SerialReader = SerialReader(client_port, SERIAL_SETTINGS_V4, telegram_specifications.V4) self.AsyncSerialReader = AsyncSerialReader(client_port, SERIAL_SETTINGS_V4, telegram_specifications.V4) cmd = [ 'socat', '-d', '-d', f'PTY,link={device_port},raw,echo=0', f'PTY,link={client_port},raw,echo=0' ] self.socat_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.dsmr_emulator_proc = multiprocessing.Process(target=run_emulator, args=(device_port, ), kwargs={ 'n_steps': 1, 'interval': .1 }) self.dsmr_emulator_proc.start()
# 'delta_hourly_gas_meter_reading', 'hourly_gas_meter_reading') # gas_meter_reading_old = retrieve_current_value_from_metric_names( # 'delta_ggas_meter_reading', 'hourly_gas_meter_reading') # # print('current values: ') # print('electricity_used_tariff_1: ' + str(electricity_used_tariff_1_old)) # print('electricity_used_tariff_2: ' + str(electricity_used_tariff_2_old)) # print('electricity_used_tariff_1_2: ' + str(electricity_used_tariff_1_2_old)) # print('electricity_delivered_tariff_1: ' + str(electricity_delivered_tariff_1_old)) # print('electricity_delivered_tariff_2: ' + str(electricity_delivered_tariff_2_old)) # print('electricity_delivered_tariff_1_2: ' + str(electricity_delivered_tariff_1_2_old)) # print('hourly_gas_meter_reading: ' + str(hourly_gas_meter_reading_old)) # print('gas_meter_reading: ' + str(gas_meter_reading_old)) serial_reader = SerialReader(device = device, serial_settings = serial_settings, telegram_specification = telegram_specification) got_socket = False while True: try: for telegram in serial_reader.read(): t_start = time.time() #print(str(telegram[obis_references.P1_MESSAGE_TIMESTAMP].value)) t = '' n = -1 for m in metrics: p = '' n = n + 1 if m == "p1_message_header": try:
self.telegram[obiref.SHORT_POWER_FAILURE_COUNT].value) except KeyError: # Not all meters provide this data pass yield CounterMetricFamily('gas_used_m3', 'Gas delivered to client in m3.', self.telegram[obiref.HOURLY_GAS_METER_READING].value) def read(self): for telegram in reader.read(): self.telegram = telegram if __name__ == '__main__': reader = SerialReader( device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4 ) collector = P1Collector(reader) registry = CollectorRegistry() registry.register(collector) start_http_server(8000, registry=registry) while True: collector.read()
from influxdb import InfluxDBClient import pprint import config import decimal import time prev_gas = None while True: try: #influx db settings db = InfluxDBClient(config.host, config.port, config.username, config.password, config.database) #serial port settings and version serial_reader = SerialReader( device=config.serial_port, serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4) print("Connecting db") db.create_database('energy') #read telegrams print("Waiting for P1 port measurement..") for telegram in serial_reader.read(): influx_measurement = { "measurement": "P1 values", # "tags": { # "host": "server01", # "region": "us-west" # },
from dsmr_parser.clients import SerialReader, SERIAL_SETTINGS_V2_2 from dsmr_parser import obis_references import json import paho.mqtt.client as mqtt import paho.mqtt.publish as mqtt_publish import time MQTT_HOSTNAME = 'localhost' MQTT_PORT = 1883 MQTT_TOPIC = 'dsmr/up' MQTT_CLIENT_ID = 'dsmr1' serial_reader = SerialReader( device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V2_2, telegram_specification=telegram_specifications.V2_2) def publish(payload): return mqtt_publish.single(MQTT_TOPIC, payload=payload, qos=0, retain=False, hostname=MQTT_HOSTNAME, port=MQTT_PORT, client_id=MQTT_CLIENT_ID, keepalive=60, will=None, protocol=mqtt.MQTTv311,
class energyMGMT: def __init__(self): self.serial_reader = SerialReader( device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V5, telegram_specification=telegram_specifications.V4 ) self.general = 'P1_log.csv' self.minute = 'P1_log.minute' self.hour = 'P1_log.hour' self.day = 'P1_log.day' self.verbose, self.logging = False, False self.powerhist = [] self.lastlog = {self.general: 0, self.minute: 0, self.hour: 0, self.day: 0} try: arg = sys.argv[1] if arg == '-v': self.verbose = True elif arg == '-l': self.logging = True except IndexError: pass def log(self, msg, file='P1_log.csv'): """ Appends message to log file :param msg: :param file: :return: """ with open(f'{os.path.abspath(os.path.dirname(__file__))}/{file}', 'a') as logfile: logfile.write(f'{time.time()},{msg}') self.lastlog[file] = time.time() def write_register(self, payload: str): """ Overwrites the current state file :param payload: :return: """ with open(f'{os.path.abspath(os.path.dirname(__file__))}/P1.state', 'w') as register: register.write(str(payload)) def trim_logs(self, lines_per_entry=21, minute_entries=1440, hour_entries=8760): os.system( f'echo "$(tail -{lines_per_entry * minute_entries} {os.path.abspath(os.path.dirname(__file__))}/{self.minute})" > {os.path.abspath(os.path.dirname(__file__))}/{self.minute}') os.system( f'echo "$(tail -{lines_per_entry * hour_entries} {os.path.abspath(os.path.dirname(__file__))}/{self.hour})" > {os.path.abspath(os.path.dirname(__file__))}/{self.hour}') def subscribe(self): for telegram in self.serial_reader.read_as_object(): if self.verbose: os.system('clear') print(telegram) else: pass if self.logging: # Updates every cycle self.write_register(f'{telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value},' f'{telegram.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value},' f'{telegram.CURRENT_ELECTRICITY_USAGE.value},' f'{telegram.CURRENT_ELECTRICITY_DELIVERY.value},' f'{telegram.ELECTRICITY_USED_TARIFF_1.value},' f'{telegram.ELECTRICITY_USED_TARIFF_2.value},' f'{telegram.ELECTRICITY_DELIVERED_TARIFF_1.value},' f'{telegram.ELECTRICITY_DELIVERED_TARIFF_2.value},' f'{telegram.HOURLY_GAS_METER_READING.value}') log_msg = '' for attr, value in telegram: if 'LOG' not in attr: log_msg += f',{telegram.P1_MESSAGE_TIMESTAMP.value},{attr},{value.value},{value.unit},\n' # Updates every minute if time.time() - self.lastlog[self.minute] >= 60: self.log(msg=log_msg[1:], file=self.minute) # self.trim_logs(lines_per_entry=1, minute_entries=1) # Updates every hour if time.time() - self.lastlog[self.hour] >= 60*60: self.log(msg=log_msg[1:], file=self.hour) # Updates every day if time.time() - self.lastlog[self.day] >= 60*60*24: self.log(msg=log_msg[1:], file=self.day) # Trim log files once a day self.trim_logs()
def __init__(self, device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V2_2, telegram_specification=telegram_specifications.V2_2, debug=False): self.device = device self.serial_settings = serial_settings self.telegram_specification = telegram_specification self.debug = debug self.serial_reader = SerialReader( device=self.device, serial_settings=self.serial_settings, telegram_specification=self.telegram_specification) self.obis_mapping_json = [ ['power_consumption', obis_ref.CURRENT_ELECTRICITY_USAGE], ['power_production', obis_ref.CURRENT_ELECTRICITY_DELIVERY], ['power_tariff', obis_ref.ELECTRICITY_ACTIVE_TARIFF], ['power_consumption_low', obis_ref.ELECTRICITY_USED_TARIFF_1], ['power_consumption_normal', obis_ref.ELECTRICITY_USED_TARIFF_2], ['power_production_low', obis_ref.ELECTRICITY_DELIVERED_TARIFF_1], [ 'power_production_normal', obis_ref.ELECTRICITY_DELIVERED_TARIFF_2 ], ['gas_consumption', obis_ref.GAS_METER_READING] ] self.obis_mapping = [ ['Power Consumption', obis_ref.CURRENT_ELECTRICITY_USAGE], ['Power Production', obis_ref.CURRENT_ELECTRICITY_DELIVERY], ['Power Tariff', obis_ref.ELECTRICITY_ACTIVE_TARIFF], ['Power Consumption (low)', obis_ref.ELECTRICITY_USED_TARIFF_1], ['Power Consumption (normal)', obis_ref.ELECTRICITY_USED_TARIFF_2], [ 'Power Production (low)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_1 ], [ 'Power Production (normal)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_2 ] # , # [ # 'Power Consumption Phase L1', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE # ], # [ # 'Power Consumption Phase L2', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE # ], # [ # 'Power Consumption Phase L3', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE # ], # [ # 'Power Production Phase L1', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE # ], # [ # 'Power Production Phase L2', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE # ], # [ # 'Power Production Phase L3', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE # ], # [ # 'Long Power Failure Count', # obis_ref.LONG_POWER_FAILURE_COUNT # ], # [ # 'Voltage Sags Phase L1', # obis_ref.VOLTAGE_SAG_L1_COUNT # ], # [ # 'Voltage Sags Phase L2', # obis_ref.VOLTAGE_SAG_L2_COUNT # ], # [ # 'Voltage Sags Phase L3', # obis_ref.VOLTAGE_SAG_L3_COUNT # ], # [ # 'Voltage Swells Phase L1', # obis_ref.VOLTAGE_SWELL_L1_COUNT # ], # [ # 'Voltage Swells Phase L2', # obis_ref.VOLTAGE_SWELL_L2_COUNT # ], # [ # 'Voltage Swells Phase L3', # obis_ref.VOLTAGE_SWELL_L3_COUNT # ], # [ # 'Voltage Phase L1', # obis_ref.INSTANTANEOUS_VOLTAGE_L1 # ], # [ # 'Voltage Phase L2', # obis_ref.INSTANTANEOUS_VOLTAGE_L2 # ], # [ # 'Voltage Phase L3', # obis_ref.INSTANTANEOUS_VOLTAGE_L3 # ], , ['Gas Consumption', obis_ref.GAS_METER_READING] ] # self.entities = [Entity(name, obis) for name, obis in self.obis_mapping] self.entities = [ Entity(name, obis) for name, obis in self.obis_mapping_json ]
class DSMRReader(): def __init__(self, device='/dev/ttyUSB0', serial_settings=SERIAL_SETTINGS_V2_2, telegram_specification=telegram_specifications.V2_2, debug=False): self.device = device self.serial_settings = serial_settings self.telegram_specification = telegram_specification self.debug = debug self.serial_reader = SerialReader( device=self.device, serial_settings=self.serial_settings, telegram_specification=self.telegram_specification) self.obis_mapping_json = [ ['power_consumption', obis_ref.CURRENT_ELECTRICITY_USAGE], ['power_production', obis_ref.CURRENT_ELECTRICITY_DELIVERY], ['power_tariff', obis_ref.ELECTRICITY_ACTIVE_TARIFF], ['power_consumption_low', obis_ref.ELECTRICITY_USED_TARIFF_1], ['power_consumption_normal', obis_ref.ELECTRICITY_USED_TARIFF_2], ['power_production_low', obis_ref.ELECTRICITY_DELIVERED_TARIFF_1], [ 'power_production_normal', obis_ref.ELECTRICITY_DELIVERED_TARIFF_2 ], ['gas_consumption', obis_ref.GAS_METER_READING] ] self.obis_mapping = [ ['Power Consumption', obis_ref.CURRENT_ELECTRICITY_USAGE], ['Power Production', obis_ref.CURRENT_ELECTRICITY_DELIVERY], ['Power Tariff', obis_ref.ELECTRICITY_ACTIVE_TARIFF], ['Power Consumption (low)', obis_ref.ELECTRICITY_USED_TARIFF_1], ['Power Consumption (normal)', obis_ref.ELECTRICITY_USED_TARIFF_2], [ 'Power Production (low)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_1 ], [ 'Power Production (normal)', obis_ref.ELECTRICITY_DELIVERED_TARIFF_2 ] # , # [ # 'Power Consumption Phase L1', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE # ], # [ # 'Power Consumption Phase L2', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE # ], # [ # 'Power Consumption Phase L3', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE # ], # [ # 'Power Production Phase L1', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE # ], # [ # 'Power Production Phase L2', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE # ], # [ # 'Power Production Phase L3', # obis_ref.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE # ], # [ # 'Long Power Failure Count', # obis_ref.LONG_POWER_FAILURE_COUNT # ], # [ # 'Voltage Sags Phase L1', # obis_ref.VOLTAGE_SAG_L1_COUNT # ], # [ # 'Voltage Sags Phase L2', # obis_ref.VOLTAGE_SAG_L2_COUNT # ], # [ # 'Voltage Sags Phase L3', # obis_ref.VOLTAGE_SAG_L3_COUNT # ], # [ # 'Voltage Swells Phase L1', # obis_ref.VOLTAGE_SWELL_L1_COUNT # ], # [ # 'Voltage Swells Phase L2', # obis_ref.VOLTAGE_SWELL_L2_COUNT # ], # [ # 'Voltage Swells Phase L3', # obis_ref.VOLTAGE_SWELL_L3_COUNT # ], # [ # 'Voltage Phase L1', # obis_ref.INSTANTANEOUS_VOLTAGE_L1 # ], # [ # 'Voltage Phase L2', # obis_ref.INSTANTANEOUS_VOLTAGE_L2 # ], # [ # 'Voltage Phase L3', # obis_ref.INSTANTANEOUS_VOLTAGE_L3 # ], , ['Gas Consumption', obis_ref.GAS_METER_READING] ] # self.entities = [Entity(name, obis) for name, obis in self.obis_mapping] self.entities = [ Entity(name, obis) for name, obis in self.obis_mapping_json ] def __update_entities_telegram(self, telegram): ''' Update entities with latest telegram and trigger state update. ''' # Make all entities aware of new telegram for entity in self.entities: entity.telegram = telegram def read(self): ''' Read DSMR telegrams ''' try: if self.debug: print('>> Reading DSMR in debug mode') for telegram in self.serial_reader.read(): self.__update_entities_telegram(telegram) data = dict() # the combination of entities == dict/json for entity in self.entities: if entity.unit == 'kW': value = float(entity.state()) * 1000 else: value = entity.state() # print(f'{entity.name} == {value} {entity.unit or ""}') data[entity.name] = f'{value} {entity.unit or ""}'.strip() print(json.dumps(data, indent=2)) # print('=' * 80) except Exception as e: print(f'[ERROR] {str(e)}')
def handle(self, *args, **options): serial_reader = SerialReader( device=options['device'], serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4 ) for telegram in serial_reader.read(): message_datetime = telegram[P1_MESSAGE_TIMESTAMP] tariff = telegram[ELECTRICITY_ACTIVE_TARIFF] tariff = int(tariff.value) electricity_used_total \ = telegram[ELECTRICITY_USED_TARIFF_ALL[tariff - 1]] electricity_delivered_total = \ telegram[ELECTRICITY_DELIVERED_TARIFF_ALL[tariff - 1]] electricity_used_actual = \ telegram[CURRENT_ELECTRICITY_USAGE] electricity_delivered_actual = \ telegram[CURRENT_ELECTRICITY_DELIVERY] gas_metric = telegram[HOURLY_GAS_METER_READING] electricity_used_tariff = \ Meter.manager.electricity_used_tariff(tariff) electricity_delivered_tariff = \ Meter.manager.electricity_delivered_tariff(tariff) gas_tariff = Meter.manager.gas_tariff() Meter.manager.electricity_used().metrics_actual.create( datetime=message_datetime.value, value=electricity_used_actual.value ) if _is_new_hourly_metric(electricity_used_tariff, message_datetime.value): electricity_used_tariff.metrics_total.create( datetime=message_datetime.value, value_total=electricity_used_total.value ) if electricity_delivered_total.value: Meter.manager.electricity_delivered().metrics_actual.create( datetime=message_datetime.value, value=electricity_delivered_actual.value ) if _is_new_hourly_metric(electricity_delivered_tariff, message_datetime.value): electricity_delivered_tariff.metrics_total.create( datetime=message_datetime.value, value_total=electricity_delivered_total.value ) if _is_new_hourly_metric(gas_tariff, gas_metric.datetime): gas_tariff.metrics_total.create( value_total=gas_metric.value, datetime=gas_metric.datetime )
from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS from .config import general_config, influx_config influx_client = None if influx_config.enabled: influx_client = InfluxDBClient( url=str(influx_config.URL), token=influx_config.TOKEN.get_secret_value(), ) influx_write_api = influx_client.write_api(write_options=SYNCHRONOUS) serial_reader = SerialReader( device=general_config.SERIAL_DEVICE, serial_settings=SERIAL_SETTINGS_V4, telegram_specification=telegram_specifications.V4, ) def read_once(): return extract_measurement(telegram=next(serial_reader.read_as_object())) def read_stream(persist=False): for telegram in serial_reader.read_as_object(): measurement = extract_measurement(telegram=telegram) if persist: push_to_influx(measurement) yield measurement
from dsmr_parser.clients import SerialReader, SERIAL_SETTINGS_V5 from dsmr_parser.objects import CosemObject, MBusObject, Telegram from dsmr_parser.parsers import TelegramParser import os, random, time import yaml import paho.mqtt.client as mqtt valuesOfInterest = os.getenv( "VALUES_OF_INTEREST", "CURRENT_ELECTRICITY_USAGE,ELECTRICITY_USED_TARIFF_ALL,HOURLY_GAS_METER_READING" ).split(',') topicStart = os.getenv('MQTT_TOPICSTART', "home/smart_meter/") # Set the parameters for dsmr_parser serial_reader = SerialReader(device=os.getenv('DSMR_DEVICE', '/dev/ttyUSB0'), serial_settings=SERIAL_SETTINGS_V5, telegram_specification=telegram_specifications.V5) # Initiate the mqtt connection client = mqtt.Client(os.getenv('MQTT_CLIENT_NAME', "sim")) if os.getenv('MQTT_USER'): client.username_pw_set(os.getenv('MQTT_USER'), password=os.getenv('MQTT_PASSWORD', "")) client.reconnect_delay_set(min_delay=1, max_delay=120) client.connect(os.getenv('MQTT_BROKER', "mqtt")) # Get all the telegram's values into a dictionary for telegram in serial_reader.read_as_object(): for attr, value in telegram: if (attr in valuesOfInterest): topic = topicStart + attr