├── README.md ├── bin ├── frsmsimulator.py └── frsmsimulator.py.bak ├── config └── config.cfg ├── cron ├── cron.10min ├── cron.hourly └── crontab ├── daemon ├── daemon └── daemon.bak ├── data └── sampledata.dat ├── dpkg ├── .swp ├── apt └── apt.bak ├── icons ├── README.txt ├── icon_128.png ├── icon_256.png ├── icon_512.png └── icon_64.png ├── plugin.cfg ├── postinstall.sh ├── postroot.sh ├── postupgrade.sh ├── preinstall.sh ├── prerelease.cfg ├── preroot.sh ├── preupgrade.sh ├── release.cfg ├── sudoers └── sudoers ├── templates ├── help │ └── help.html ├── index.html ├── index.html.bak ├── lang │ ├── help_de.ini │ ├── help_en.ini │ ├── language_de.ini │ └── language_en.ini └── sample.html ├── uninstall ├── uninstall └── uninstall.sh └── webfrontend ├── html ├── .dummy └── index.cgi └── htmlauth ├── index.cgi └── index.cgi.bak /README.md: -------------------------------------------------------------------------------- 1 | This plugin for Loxberry (https://wiki.loxberry.de/) is used to simulate a Fronius SmartMeter with MODBUS RTU protocol. 2 | 3 | Note: Text below is automaticaly translated due to lack of time :-) - so please be lenitent. 4 | 5 | NOTE: 6 | Due to the fact that Fronius changed their inverter firmware to open for Modbus TCP meters, this RTU version is not needed any more very often. 7 | So I will reduce my work regarding RTU and focus on the TCP version "fronius_sm_simulator_tcp". 8 | Everyone who want's to proceed work with RTU is welcome :-) 9 | 10 | Functional Description 11 | ====================== 12 | The plugin simulates a Fronius Smart Meter in the Modbus RTU network. This means that consumption values can be received via MQTT and forwarded to a Fronius inverter (e.g.: Symo, Symo Hybrid). The data is then permanently stored in the Solarweb statistics. 13 | 14 | Installation 15 | ============ 16 | Installation as usual as ZIP via the Loxberry installation routine. 17 | Installation can take up to 20 minutes depending on the Pi you are using 18 | 19 | Prerequisites 20 | ============= 21 | An existing SmartMeter that can be read and whose data can be transferred via MQTT. 22 | A Modbus module for the Raspberry 23 | The “MQTT Gateway” plugin must be installed and configured. 24 | 25 | Configuration 26 | ============= 27 | MQTT Topic current consumption: current consumption (current power) in watts (W) 28 | MQTT Topic Purchase (absolute value): current meter reading for purchased energy in watt hours (Wh) 29 | MQTT Topic Feed-in (absolute value): current meter reading for fed-in energy in watt hours (Wh) 30 | Correction factor: If the data is delivered in kWh via MQTT, it must be converted into Wh =⇒ Correction factor 1000 (default setting). 31 | Port of the Modbus adapter: Port where the Modbus adapter is plugged in. 32 | 33 | !!! WARNING !!! 34 | =============== 35 | The Fronius inverter does NOT use the "current power in watts" to calculate the current consumption and fill the statistics, but rather calculates this information itself using the difference between the absolute values of the purchase and feed-in in the time window of the query. The data MUST be provided via MODBUS in watt hours (see correction factor description above). If an error occurs when choosing the correction factor, the statistics in the data manager will be falsified. 36 | 37 | A falsified statistic can no longer be changed independently! 38 | 39 | Of course, this happened to me during my development attempts because I had no idea how the data had to be provided and could only have the statistics corrected by Fronius Support. 40 | 41 | Known Issues 42 | ============ 43 | Modbus communication always stopps after days at around 2:00 p.m. - 3:00 p.m. and only comes back automatically two days later. I suspect a problem with my Pi and am relying on experience from the field to see whether this also occurs for other users. A cron job that restarts the script every day at 00:01 seems to help for me. 44 | 45 | Roadmap 46 | ======= 47 | Extension to simulate multiple meters: Goal 1. Primary meter (consumption) and other secondary meters for e.g.: Integration of other energy sources (wind turbine, CHP, PV without Fronius inverter, ...) 48 | Improving usability (web interface) 49 | Upgrade to Python3 50 | DEBUG functions must be implemented 51 | Plausibility check of the data regarding kWh ⇔ Wh to avoid statistical problems. 52 | Leaving BETA status :-) 53 | -------------------------------------------------------------------------------- /bin/frsmsimulator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Fronius Smart Meter Simulator (MODBUS) Version 1.0 4 | -------------------------------------------------------------------------- 5 | 6 | This script simulates a Fronius Smart Meter for providing necessary 7 | information to inverters (e.g. SYMO, SYMO HYBRID) for statistics. 8 | Necessary information is provied via MQTT and translated to MODBUS RTU 9 | 10 | """ 11 | # --------------------------------------------------------------------------- # 12 | # import the modbus libraries we need 13 | # --------------------------------------------------------------------------- # 14 | from pymodbus.version import version 15 | from pymodbus.server.asynchronous import StartSerialServer, StopServer 16 | from pymodbus.device import ModbusDeviceIdentification 17 | from pymodbus.datastore import ModbusSequentialDataBlock 18 | from pymodbus.datastore import ModbusSparseDataBlock 19 | from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext 20 | from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer 21 | from threading import Lock 22 | import struct 23 | import time 24 | import json 25 | import getopt 26 | import sys 27 | import socket 28 | import signal 29 | import os 30 | 31 | 32 | # --------------------------------------------------------------------------- # 33 | # import the twisted libraries we need 34 | # --------------------------------------------------------------------------- # 35 | from twisted.internet.task import LoopingCall 36 | 37 | # --------------------------------------------------------------------------- # 38 | # configure the service logging 39 | # --------------------------------------------------------------------------- # 40 | import logging 41 | logging.basicConfig() 42 | log = logging.getLogger() 43 | log.setLevel(logging.DEBUG) 44 | 45 | # --------------------------------------------------------------------------- # 46 | # handle start arguments 47 | # --------------------------------------------------------------------------- # 48 | inputs = None 49 | outputs = None 50 | loglevel=logging.ERROR 51 | logfile="" 52 | logfileArg = "" 53 | lbhomedir = os.popen("perl -e 'use LoxBerry::System; print $lbhomedir; exit;'").read() 54 | configfile = "" 55 | 56 | opts, args = getopt.getopt(sys.argv[1:], 'f:l:c:h:', ['logfile=', 'loglevel=', 'configfile=', 'lbhomedir=']) 57 | for opt, arg in opts: 58 | if opt in ('-f', '--logfile'): 59 | logfile=arg 60 | logfileArg = arg 61 | elif opt in ('-l', '--loglevel'): 62 | loglevel=map_loglevel(arg) 63 | elif opt in ('-c', '--configfile'): 64 | configfile=arg 65 | elif opt in ('-h', '--lbhomedir'): 66 | lbhomedir=arg 67 | 68 | 69 | # --------------------------------------------------------------------------- # 70 | # get configuration from mqtt broker and store config in mqttconf variable 71 | # --------------------------------------------------------------------------- # 72 | mqttconf = None; 73 | 74 | MQTT_DEFAULT_PORT = "1883" 75 | 76 | #try: 77 | with open(lbhomedir + '/data/system/plugindatabase.json') as json_plugindatabase_file: 78 | plugindatabase = json.load(json_plugindatabase_file) 79 | mqttconfigdir = plugindatabase['plugins']['07a6053111afa90479675dbcd29d54b5']['directories']['lbpconfigdir'] 80 | 81 | mqttPluginconfig = None 82 | with open(mqttconfigdir + '/mqtt.json') as json_mqttconfig_file: 83 | mqttPluginconfig = json.load(json_mqttconfig_file) 84 | 85 | mqttcred = None 86 | with open(mqttconfigdir + '/cred.json') as json_mqttcred_file: 87 | mqttcred = json.load(json_mqttcred_file) 88 | 89 | mqttuser = mqttcred['Credentials']['brokeruser'] 90 | mqttpass = mqttcred['Credentials']['brokerpass'] 91 | mqttaddressArray = mqttPluginconfig['Main']['brokeraddress'].split(":") 92 | mqttPort = MQTT_DEFAULT_PORT 93 | if len(mqttaddressArray) > 1: 94 | mqttPort = int(mqttaddressArray[1]) 95 | 96 | mqttconf = { 97 | 'username':mqttuser, 98 | 'password':mqttpass, 99 | 'address': mqttaddressArray[0], 100 | 'port': mqttPort 101 | } 102 | 103 | 104 | # _LOGGER.debug("MQTT config" + str(mqttconf)) 105 | #except Exception as e: 106 | # _LOGGER.exception(str(e)) 107 | 108 | # If no mqtt config found leave the script with log entry 109 | #if mqttconf is None: 110 | # _LOGGER.critical("No MQTT config found. Daemon stop working") 111 | # sys.exit(-1) 112 | 113 | # --------------------------------------------------------------------------- # 114 | # Reading custom parameters out of paramter file 115 | # --------------------------------------------------------------------------- # 116 | import ConfigParser 117 | 118 | config = ConfigParser.RawConfigParser() 119 | config.read(lbhomedir + '/config/plugins/frosim_folder/config.cfg') 120 | 121 | MQTT_TOPIC_CONSUMPTION = config.get('CONFIGURATION','TOPIC_CONSUMPTION') 122 | MQTT_TOPIC_TOTAL_IMPORT = config.get('CONFIGURATION','TOPIC_TOTAL_IMPORT') 123 | MQTT_TOPIC_TOTAL_EXPORT = config.get('CONFIGURATION','TOPIC_TOTAL_EXPORT') 124 | corrfactor = config.get('CONFIGURATION','CORRFACTOR') 125 | i_corrfactor = int(corrfactor) 126 | serialport = config.get('CONFIGURATION','SERIAL_PORT') 127 | 128 | print MQTT_TOPIC_TOTAL_EXPORT 129 | print MQTT_TOPIC_TOTAL_IMPORT 130 | print MQTT_TOPIC_CONSUMPTION 131 | print corrfactor 132 | print serialport 133 | 134 | # --------------------------------------------------------------------------- # 135 | # configure MQTT service 136 | # --------------------------------------------------------------------------- # 137 | import paho.mqtt.client as mqtt 138 | import paho.mqtt.subscribe as subscribe 139 | 140 | lock = Lock() 141 | 142 | leistung = "0" 143 | einspeisung = "0" 144 | netzbezug = "0" 145 | 146 | ti_int1 = "0" 147 | ti_int2 = "0" 148 | exp_int1 = "0" 149 | exp_int2 = "0" 150 | ep_int1 = "0" 151 | ep_int2 = "0" 152 | 153 | mqttc = mqtt.Client() 154 | mqttc.username_pw_set(mqttconf['username'], mqttconf['password']) 155 | mqttc.connect(mqttconf['address'], mqttconf['port'], 60) 156 | 157 | mqttc.subscribe(MQTT_TOPIC_CONSUMPTION) 158 | mqttc.subscribe(MQTT_TOPIC_TOTAL_IMPORT) 159 | mqttc.subscribe(MQTT_TOPIC_TOTAL_EXPORT) 160 | 161 | mqttc.loop_start() 162 | 163 | #def terminateProcess(signalNumber, frame): 164 | # print('(SIGTERM) terminating the process') 165 | # StopServer() 166 | 167 | def on_message(client, userdata, message): 168 | global leistung 169 | global einspeisung 170 | global netzbezug 171 | 172 | print("Received message '" + str(message.payload) + "' on topic '" 173 | + message.topic + "' with QoS " + str(message.qos)) 174 | 175 | lock.acquire() 176 | 177 | if message.topic == MQTT_TOPIC_CONSUMPTION: 178 | leistung = message.payload 179 | elif message.topic == MQTT_TOPIC_TOTAL_IMPORT: 180 | netzbezug = message.payload 181 | elif message.topic == MQTT_TOPIC_TOTAL_EXPORT: 182 | einspeisung = message.payload 183 | 184 | lock.release() 185 | 186 | mqttc.on_message = on_message 187 | 188 | 189 | #def terminateProcess(signalNumber, frame): 190 | # print('(SIGTERM) terminating the process') 191 | # StopServer() 192 | 193 | # --------------------------------------------------------------------------- # 194 | # define your callback process 195 | # --------------------------------------------------------------------------- # 196 | 197 | 198 | def updating_writer(a): 199 | """ A worker process that runs every so often and 200 | updates live values of the context. It should be noted 201 | that there is a race condition for the update. 202 | 203 | :param arguments: The input arguments to the call 204 | """ 205 | 206 | global ep_int1 207 | global ep_int2 208 | global exp_int1 209 | global exp_int2 210 | global ti_int1 211 | global ti_int2 212 | 213 | lock.acquire() 214 | #Considering correction factor 215 | print("Korrigierte Werte") 216 | float_netzbezug = float(netzbezug) 217 | netzbezug_corr = float_netzbezug*i_corrfactor 218 | print netzbezug_corr 219 | 220 | float_einspeisung = float(einspeisung) 221 | einspeisung_corr = float_einspeisung*i_corrfactor 222 | print einspeisung_corr 223 | 224 | #Converting current power consumption out of MQTT payload to Modbus register 225 | 226 | electrical_power_float = float(leistung) #extract value out of payload 227 | print electrical_power_float 228 | electrical_power_hex = struct.pack('>f', electrical_power_float).encode('hex') #convert float value to float32 and further to hex 229 | electrical_power_hex_part1 = str(electrical_power_hex)[0:4] #extract first register part (hex) 230 | electrical_power_hex_part2 = str(electrical_power_hex)[4:8] #extract seconds register part (hex) 231 | ep_int1 = int(electrical_power_hex_part1, 16) #convert hex to integer because pymodbus converts back to hex itself 232 | ep_int2 = int(electrical_power_hex_part2, 16) #convert hex to integer because pymodbus converts back to hex itself 233 | 234 | #Converting total import value of smart meter out of MQTT payload into Modbus register 235 | 236 | total_import_float = int(netzbezug_corr) 237 | print total_import_float 238 | total_import_hex = struct.pack('>f', total_import_float).encode("hex") 239 | total_import_hex_part1 = str(total_import_hex)[0:4] 240 | total_import_hex_part2 = str(total_import_hex)[4:8] 241 | ti_int1 = int(total_import_hex_part1, 16) 242 | ti_int2 = int(total_import_hex_part2, 16) 243 | 244 | #Converting total export value of smart meter out of MQTT payload into Modbus register 245 | 246 | total_export_float = int(einspeisung_corr) 247 | print total_export_float 248 | total_export_hex = struct.pack('>f', total_export_float).encode("hex") 249 | total_export_hex_part1 = str(total_export_hex)[0:4] 250 | total_export_hex_part2 = str(total_export_hex)[4:8] 251 | exp_int1 = int(total_export_hex_part1, 16) 252 | exp_int2 = int(total_export_hex_part2, 16) 253 | 254 | log.debug("updating the context") 255 | context = a[0] 256 | register = 3 257 | slave_id = 0x01 258 | address = 0x9C87 259 | values = [0, 0, #Ampere - AC Total Current Value [A] 260 | 0, 0, #Ampere - AC Current Value L1 [A] 261 | 0, 0, #Ampere - AC Current Value L2 [A] 262 | 0, 0, #Ampere - AC Current Value L3 [A] 263 | 0, 0, #Voltage - Average Phase to Neutral [V] 264 | 0, 0, #Voltage - Phase L1 to Neutral [V] 265 | 0, 0, #Voltage - Phase L2 to Neutral [V] 266 | 0, 0, #Voltage - Phase L3 to Neutral [V] 267 | 0, 0, #Voltage - Average Phase to Phase [V] 268 | 0, 0, #Voltage - Phase L1 to L2 [V] 269 | 0, 0, #Voltage - Phase L2 to L3 [V] 270 | 0, 0, #Voltage - Phase L1 to L3 [V] 271 | 0, 0, #AC Frequency [Hz] 272 | ep_int1, 0, #AC Power value (Total) [W] ==> Second hex word not needed 273 | 0, 0, #AC Power Value L1 [W] 274 | 0, 0, #AC Power Value L2 [W] 275 | 0, 0, #AC Power Value L3 [W] 276 | 0, 0, #AC Apparent Power [VA] 277 | 0, 0, #AC Apparent Power L1 [VA] 278 | 0, 0, #AC Apparent Power L2 [VA] 279 | 0, 0, #AC Apparent Power L3 [VA] 280 | 0, 0, #AC Reactive Power [VAr] 281 | 0, 0, #AC Reactive Power L1 [VAr] 282 | 0, 0, #AC Reactive Power L2 [VAr] 283 | 0, 0, #AC Reactive Power L3 [VAr] 284 | 0, 0, #AC power factor total [cosphi] 285 | 0, 0, #AC power factor L1 [cosphi] 286 | 0, 0, #AC power factor L2 [cosphi] 287 | 0, 0, #AC power factor L3 [cosphi] 288 | exp_int1, exp_int2, #Total Watt Hours Exportet [Wh] 289 | 0, 0, #Watt Hours Exported L1 [Wh] 290 | 0, 0, #Watt Hours Exported L2 [Wh] 291 | 0, 0, #Watt Hours Exported L3 [Wh] 292 | ti_int1, ti_int2, #Total Watt Hours Imported [Wh] 293 | 0, 0, #Watt Hours Imported L1 [Wh] 294 | 0, 0, #Watt Hours Imported L2 [Wh] 295 | 0, 0, #Watt Hours Imported L3 [Wh] 296 | 0, 0, #Total VA hours Exported [VA] 297 | 0, 0, #VA hours Exported L1 [VA] 298 | 0, 0, #VA hours Exported L2 [VA] 299 | 0, 0, #VA hours Exported L3 [VA] 300 | 0, 0, #Total VAr hours imported [VAr] 301 | 0, 0, #VA hours imported L1 [VAr] 302 | 0, 0, #VA hours imported L2 [VAr] 303 | 0, 0 #VA hours imported L3 [VAr] 304 | ] 305 | 306 | context[slave_id].setValues(register, address, values) 307 | 308 | lock.release() 309 | 310 | # signal.signal(signal.SIGTERM,terminateProcess) 311 | 312 | def run_updating_server(): 313 | # ----------------------------------------------------------------------- # 314 | # initialize your data store 315 | # ----------------------------------------------------------------------- # 316 | lock.acquire() 317 | 318 | store = ModbusSlaveContext( 319 | di=ModbusSequentialDataBlock(0, [15]*100), 320 | co=ModbusSequentialDataBlock(0, [15]*100), 321 | hr=ModbusSparseDataBlock({ 322 | 323 | 40001: [21365, 28243], 324 | 40003: [1], 325 | 40004: [65], 326 | 40005: [70,114,111,110,105,117,115,0,0,0,0,0,0,0,0,0, #Manufacturer "Fronius" 327 | 83,109,97,114,116,32,77,101,116,101,114,32,54,51,65,0, #Device Model "Smart Meter 63A" 328 | 0,0,0,0,0,0,0,0, #Options N/A 329 | 0,0,0,0,0,0,0,0, #Software Version N/A 330 | 48,48,48,48,48,48,48,49,0,0,0,0,0,0,0,0, #Serial Number: 00000001 (49,54,50,50,48,49,49,56 331 | 240], #Modbus TCP Address: 240 332 | 40070: [213], 333 | 40071: [124], 334 | 40072: [0,0,0,0,0,0,0,0,0,0, 335 | 0,0,0,0,0,0,0,0,0,0, 336 | 0,0,0,0,0,0,0,0,0,0, 337 | 0,0,0,0,0,0,0,0,0,0, 338 | 0,0,0,0,0,0,0,0,0,0, 339 | 0,0,0,0,0,0,0,0,0,0, 340 | 0,0,0,0,0,0,0,0,0,0, 341 | 0,0,0,0,0,0,0,0,0,0, 342 | 0,0,0,0,0,0,0,0,0,0, 343 | 0,0,0,0,0,0,0,0,0,0, 344 | 0,0,0,0,0,0,0,0,0,0, 345 | 0,0,0,0,0,0,0,0,0,0, 346 | 0,0,0,0], 347 | 348 | 40196: [65535, 0], 349 | 350 | }), 351 | 352 | ir=ModbusSequentialDataBlock(0, [15]*100)) 353 | context = ModbusServerContext(slaves=store, single=True) 354 | 355 | lock.release() 356 | 357 | # ----------------------------------------------------------------------- # 358 | # run the server you want 359 | # ----------------------------------------------------------------------- # 360 | time = 5 # 5 seconds delay 361 | loop = LoopingCall(f=updating_writer, a=(context,)) 362 | loop.start(time, now=True) # initially delay by time 363 | 364 | StartSerialServer(context, port=serialport, baudrate=9600, stopbits=1, bytesize=8, framer=ModbusRtuFramer) 365 | 366 | 367 | values_ready = False 368 | 369 | while not values_ready: 370 | print("Warten auf Daten von MQTT Broker") 371 | time.sleep(1) 372 | lock.acquire() 373 | if netzbezug != '0' and einspeisung != '0': 374 | print("Daten vorhanden. Starte Modbus Server") 375 | values_ready = True 376 | lock.release() 377 | run_updating_server() 378 | -------------------------------------------------------------------------------- /bin/frsmsimulator.py.bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Fronius Smart Meter Simulator (MODBUS) Version 1.0 4 | -------------------------------------------------------------------------- 5 | 6 | This script simulates a Fronius Smart Meter for providing necessary 7 | information to inverters (e.g. SYMO, SYMO HYBRID) for statistics. 8 | Necessary information is provied via MQTT and translated to MODBUS RTU 9 | 10 | """ 11 | # --------------------------------------------------------------------------- # 12 | # import the modbus libraries we need 13 | # --------------------------------------------------------------------------- # 14 | from pymodbus.version import version 15 | from pymodbus.server.asynchronous import StartSerialServer 16 | from pymodbus.device import ModbusDeviceIdentification 17 | from pymodbus.datastore import ModbusSequentialDataBlock 18 | from pymodbus.datastore import ModbusSparseDataBlock 19 | from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext 20 | from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer 21 | from threading import Lock 22 | import struct 23 | import time 24 | import json 25 | import getopt 26 | import sys 27 | import socket 28 | 29 | # --------------------------------------------------------------------------- # 30 | # import the twisted libraries we need 31 | # --------------------------------------------------------------------------- # 32 | from twisted.internet.task import LoopingCall 33 | 34 | # --------------------------------------------------------------------------- # 35 | # configure the service logging 36 | # --------------------------------------------------------------------------- # 37 | import logging 38 | logging.basicConfig() 39 | log = logging.getLogger() 40 | log.setLevel(logging.DEBUG) 41 | 42 | # --------------------------------------------------------------------------- # 43 | # handle start arguments 44 | # --------------------------------------------------------------------------- # 45 | inputs = None 46 | outputs = None 47 | loglevel=logging.ERROR 48 | logfile="" 49 | logfileArg = "" 50 | lbhomedir = "" 51 | configfile = "" 52 | opts, args = getopt.getopt(sys.argv[1:], 'f:l:c:h:', ['logfile=', 'loglevel=', 'configfile=', 'lbhomedir=']) 53 | for opt, arg in opts: 54 | if opt in ('-f', '--logfile'): 55 | logfile=arg 56 | logfileArg = arg 57 | elif opt in ('-l', '--loglevel'): 58 | loglevel=map_loglevel(arg) 59 | elif opt in ('-c', '--configfile'): 60 | configfile=arg 61 | elif opt in ('-h', '--lbhomedir'): 62 | lbhomedir=arg 63 | 64 | # --------------------------------------------------------------------------- # 65 | # get configuration from mqtt broker and store config in mqttconf variable 66 | # --------------------------------------------------------------------------- # 67 | mqttconf = None; 68 | 69 | MQTT_DEFAULT_PORT = "1883" 70 | 71 | #try: 72 | with open(lbhomedir + '/data/system/plugindatabase.json') as json_plugindatabase_file: 73 | plugindatabase = json.load(json_plugindatabase_file) 74 | mqttconfigdir = plugindatabase['plugins']['07a6053111afa90479675dbcd29d54b5']['directories']['lbpconfigdir'] 75 | 76 | mqttPluginconfig = None 77 | with open(mqttconfigdir + '/mqtt.json') as json_mqttconfig_file: 78 | mqttPluginconfig = json.load(json_mqttconfig_file) 79 | 80 | mqttcred = None 81 | with open(mqttconfigdir + '/cred.json') as json_mqttcred_file: 82 | mqttcred = json.load(json_mqttcred_file) 83 | 84 | mqttuser = mqttcred['Credentials']['brokeruser'] 85 | mqttpass = mqttcred['Credentials']['brokerpass'] 86 | mqttaddressArray = mqttPluginconfig['Main']['brokeraddress'].split(":") 87 | mqttPort = MQTT_DEFAULT_PORT 88 | if len(mqttaddressArray) > 1: 89 | mqttPort = int(mqttaddressArray[1]) 90 | 91 | mqttconf = { 92 | 'username':mqttuser, 93 | 'password':mqttpass, 94 | 'address': mqttaddressArray[0], 95 | 'port': mqttPort 96 | } 97 | 98 | 99 | # _LOGGER.debug("MQTT config" + str(mqttconf)) 100 | #except Exception as e: 101 | # _LOGGER.exception(str(e)) 102 | 103 | # If no mqtt config found leave the script with log entry 104 | #if mqttconf is None: 105 | # _LOGGER.critical("No MQTT config found. Daemon stop working") 106 | # sys.exit(-1) 107 | 108 | # --------------------------------------------------------------------------- # 109 | # Reading custom parameters out of paramter file 110 | # --------------------------------------------------------------------------- # 111 | import ConfigParser 112 | 113 | config = ConfigParser.RawConfigParser() 114 | config.read(lbhomedir + '/config/plugins/frosim_folder/config.cfg') 115 | 116 | MQTT_TOPIC_CONSUMPTION = config.get('CONFIGURATION','TOPIC_CONSUMPTION') 117 | MQTT_TOPIC_TOTAL_IMPORT = config.get('CONFIGURATION','TOPIC_TOTAL_IMPORT') 118 | MQTT_TOPIC_TOTAL_EXPORT = config.get('CONFIGURATION','TOPIC_TOTAL_EXPORT') 119 | corrfactor = config.get('CONFIGURATION','CORRFACTOR') 120 | i_corrfactor = int(corrfactor) 121 | serialport = config.get('CONFIGURATION',"SERIALPORT') 122 | 123 | print MQTT_TOPIC_TOTAL_EXPORT 124 | print MQTT_TOPIC_TOTAL_IMPORT 125 | print MQTT_TOPIC_CONSUMPTION 126 | print corrfactor 127 | print serialport 128 | 129 | # --------------------------------------------------------------------------- # 130 | # configure MQTT service 131 | # --------------------------------------------------------------------------- # 132 | import paho.mqtt.client as mqtt 133 | import paho.mqtt.subscribe as subscribe 134 | 135 | lock = Lock() 136 | 137 | leistung = "0" 138 | einspeisung = "0" 139 | netzbezug = "0" 140 | 141 | ti_int1 = "0" 142 | ti_int2 = "0" 143 | exp_int1 = "0" 144 | exp_int2 = "0" 145 | ep_int1 = "0" 146 | ep_int2 = "0" 147 | 148 | mqttc = mqtt.Client() 149 | mqttc.username_pw_set(mqttconf['username'], mqttconf['password']) 150 | mqttc.connect(mqttconf['address'], mqttconf['port'], 60) 151 | 152 | mqttc.subscribe(MQTT_TOPIC_CONSUMPTION) 153 | mqttc.subscribe(MQTT_TOPIC_TOTAL_IMPORT) 154 | mqttc.subscribe(MQTT_TOPIC_TOTAL_EXPORT) 155 | 156 | mqttc.loop_start() 157 | 158 | def on_message(client, userdata, message): 159 | global leistung 160 | global einspeisung 161 | global netzbezug 162 | 163 | print("Received message '" + str(message.payload) + "' on topic '" 164 | + message.topic + "' with QoS " + str(message.qos)) 165 | 166 | lock.acquire() 167 | 168 | if message.topic == MQTT_TOPIC_CONSUMPTION: 169 | leistung = message.payload 170 | elif message.topic == MQTT_TOPIC_TOTAL_IMPORT: 171 | netzbezug = message.payload 172 | elif message.topic == MQTT_TOPIC_TOTAL_EXPORT: 173 | einspeisung = message.payload 174 | 175 | lock.release() 176 | 177 | 178 | mqttc.on_message = on_message 179 | 180 | # --------------------------------------------------------------------------- # 181 | # define your callback process 182 | # --------------------------------------------------------------------------- # 183 | 184 | 185 | def updating_writer(a): 186 | """ A worker process that runs every so often and 187 | updates live values of the context. It should be noted 188 | that there is a race condition for the update. 189 | 190 | :param arguments: The input arguments to the call 191 | """ 192 | 193 | global ep_int1 194 | global ep_int2 195 | global exp_int1 196 | global exp_int2 197 | global ti_int1 198 | global ti_int2 199 | 200 | #Considering correction factor 201 | print("Korrigierte Werte") 202 | float_netzbezug = float(netzbezug) 203 | netzbezug_corr = float_netzbezug*i_corrfactor 204 | print netzbezug_corr 205 | 206 | float_einspeisung = float(einspeisung) 207 | einspeisung_corr = float_einspeisung*i_corrfactor 208 | print einspeisung_corr 209 | 210 | #Converting current power consumption out of MQTT payload to Modbus register 211 | lock.acquire() 212 | electrical_power_float = float(leistung) #extract value out of payload 213 | print electrical_power_float 214 | electrical_power_hex = struct.pack('>f', electrical_power_float).encode('hex') #convert float value to float32 and further to hex 215 | electrical_power_hex_part1 = str(electrical_power_hex)[0:4] #extract first register part (hex) 216 | electrical_power_hex_part2 = str(electrical_power_hex)[4:8] #extract seconds register part (hex) 217 | ep_int1 = int(electrical_power_hex_part1, 16) #convert hex to integer because pymodbus converts back to hex itself 218 | ep_int2 = int(electrical_power_hex_part2, 16) #convert hex to integer because pymodbus converts back to hex itself 219 | 220 | #Converting total import value of smart meter out of MQTT payload into Modbus register 221 | 222 | total_import_float = int(netzbezug_corr) 223 | print total_import_float 224 | total_import_hex = struct.pack('>f', total_import_float).encode("hex") 225 | total_import_hex_part1 = str(total_import_hex)[0:4] 226 | total_import_hex_part2 = str(total_import_hex)[4:8] 227 | ti_int1 = int(total_import_hex_part1, 16) 228 | ti_int2 = int(total_import_hex_part2, 16) 229 | 230 | #Converting total export value of smart meter out of MQTT payload into Modbus register 231 | 232 | total_export_float = int(einspeisung_corr) 233 | print total_export_float 234 | total_export_hex = struct.pack('>f', total_export_float).encode("hex") 235 | total_export_hex_part1 = str(total_export_hex)[0:4] 236 | total_export_hex_part2 = str(total_export_hex)[4:8] 237 | exp_int1 = int(total_export_hex_part1, 16) 238 | exp_int2 = int(total_export_hex_part2, 16) 239 | 240 | lock.release() 241 | 242 | log.debug("updating the context") 243 | context = a[0] 244 | register = 3 245 | slave_id = 0x01 246 | address = 0x9C87 247 | values = [0, 0, #Ampere - AC Total Current Value [A] 248 | 0, 0, #Ampere - AC Current Value L1 [A] 249 | 0, 0, #Ampere - AC Current Value L2 [A] 250 | 0, 0, #Ampere - AC Current Value L3 [A] 251 | 0, 0, #Voltage - Average Phase to Neutral [V] 252 | 0, 0, #Voltage - Phase L1 to Neutral [V] 253 | 0, 0, #Voltage - Phase L2 to Neutral [V] 254 | 0, 0, #Voltage - Phase L3 to Neutral [V] 255 | 0, 0, #Voltage - Average Phase to Phase [V] 256 | 0, 0, #Voltage - Phase L1 to L2 [V] 257 | 0, 0, #Voltage - Phase L2 to L3 [V] 258 | 0, 0, #Voltage - Phase L1 to L3 [V] 259 | 0, 0, #AC Frequency [Hz] 260 | ep_int1, 0, #AC Power value (Total) [W] ==> Second hex word not needed 261 | 0, 0, #AC Power Value L1 [W] 262 | 0, 0, #AC Power Value L2 [W] 263 | 0, 0, #AC Power Value L3 [W] 264 | 0, 0, #AC Apparent Power [VA] 265 | 0, 0, #AC Apparent Power L1 [VA] 266 | 0, 0, #AC Apparent Power L2 [VA] 267 | 0, 0, #AC Apparent Power L3 [VA] 268 | 0, 0, #AC Reactive Power [VAr] 269 | 0, 0, #AC Reactive Power L1 [VAr] 270 | 0, 0, #AC Reactive Power L2 [VAr] 271 | 0, 0, #AC Reactive Power L3 [VAr] 272 | 0, 0, #AC power factor total [cosphi] 273 | 0, 0, #AC power factor L1 [cosphi] 274 | 0, 0, #AC power factor L2 [cosphi] 275 | 0, 0, #AC power factor L3 [cosphi] 276 | exp_int1, exp_int2, #Total Watt Hours Exportet [Wh] 277 | 0, 0, #Watt Hours Exported L1 [Wh] 278 | 0, 0, #Watt Hours Exported L2 [Wh] 279 | 0, 0, #Watt Hours Exported L3 [Wh] 280 | ti_int1, ti_int2, #Total Watt Hours Imported [Wh] 281 | 0, 0, #Watt Hours Imported L1 [Wh] 282 | 0, 0, #Watt Hours Imported L2 [Wh] 283 | 0, 0, #Watt Hours Imported L3 [Wh] 284 | 0, 0, #Total VA hours Exported [VA] 285 | 0, 0, #VA hours Exported L1 [VA] 286 | 0, 0, #VA hours Exported L2 [VA] 287 | 0, 0, #VA hours Exported L3 [VA] 288 | 0, 0, #Total VAr hours imported [VAr] 289 | 0, 0, #VA hours imported L1 [VAr] 290 | 0, 0, #VA hours imported L2 [VAr] 291 | 0, 0 #VA hours imported L3 [VAr] 292 | ] 293 | 294 | context[slave_id].setValues(register, address, values) 295 | 296 | def run_updating_server(): 297 | # ----------------------------------------------------------------------- # 298 | # initialize your data store 299 | # ----------------------------------------------------------------------- # 300 | lock.acquire() 301 | 302 | store = ModbusSlaveContext( 303 | di=ModbusSequentialDataBlock(0, [15]*100), 304 | co=ModbusSequentialDataBlock(0, [15]*100), 305 | hr=ModbusSparseDataBlock({ 306 | 307 | 40001: [21365, 28243], 308 | 40003: [1], 309 | 40004: [65], 310 | 40005: [70,114,111,110,105,117,115,0,0,0,0,0,0,0,0,0, #Manufacturer "Fronius" 311 | 83,109,97,114,116,32,77,101,116,101,114,32,54,51,65,0, #Device Model "Smart Meter 63A" 312 | 0,0,0,0,0,0,0,0, #Options N/A 313 | 0,0,0,0,0,0,0,0, #Software Version N/A 314 | 48,48,48,48,48,48,48,49,0,0,0,0,0,0,0,0, #Serial Number: 00000001 (49,54,50,50,48,49,49,56 315 | 240], #Modbus TCP Address: 240 316 | 40070: [213], 317 | 40071: [124], 318 | 40072: [0,0,0,0,0,0,0,0,0,0, 319 | 0,0,0,0,0,0,0,0,0,0, 320 | 0,0,0,0,0,0,0,0,0,0, 321 | 0,0,0,0,0,0,0,0,0,0, 322 | 0,0,0,0,0,0,0,0,0,0, 323 | 0,0,0,0,0,0,0,0,0,0, 324 | 0,0,0,0,0,0,0,0,0,0, 325 | 0,0,0,0,0,0,0,0,0,0, 326 | 0,0,0,0,0,0,0,0,0,0, 327 | 0,0,0,0,0,0,0,0,0,0, 328 | 0,0,0,0,0,0,0,0,0,0, 329 | 0,0,0,0,0,0,0,0,0,0, 330 | 0,0,0,0], 331 | 332 | 40196: [65535, 0], 333 | 334 | }), 335 | 336 | ir=ModbusSequentialDataBlock(0, [15]*100)) 337 | context = ModbusServerContext(slaves=store, single=True) 338 | 339 | lock.release() 340 | 341 | # ----------------------------------------------------------------------- # 342 | # run the server you want 343 | # ----------------------------------------------------------------------- # 344 | time = 5 # 5 seconds delay 345 | loop = LoopingCall(f=updating_writer, a=(context,)) 346 | loop.start(time, now=True) # initially delay by time 347 | 348 | StartSerialServer(context, port=serialport, baudrate=9600, stopbits=1, bytesize=8, framer=ModbusRtuFramer) 349 | 350 | values_ready = False 351 | 352 | while not values_ready: 353 | print("Warten auf Daten von MQTT Broker") 354 | time.sleep(1) 355 | lock.acquire() 356 | if netzbezug != '0' and einspeisung != '0': 357 | print("Daten vorhanden. Starte Modbus Server") 358 | values_ready = True 359 | lock.release() 360 | run_updating_server() 361 | -------------------------------------------------------------------------------- /config/config.cfg: -------------------------------------------------------------------------------- 1 | [CONFIGURATION] 2 | TOPIC_CONSUMPTION = AMIS/Leistung 3 | TOPIC_TOTAL_IMPORT = AMIS/Netzbezug_total 4 | TOPIC_TOTAL_EXPORT = AMIS/Netzeinspeisung_total 5 | CORRFACTOR = 1000 6 | SERIAL_PORT = /dev/ttyUSB0 7 | -------------------------------------------------------------------------------- /cron/cron.10min: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a sample cron file. According to it's name it will go to 4 | # ~/system/cron/cron.10min. You may also let your Pluginscript create a 5 | # symbolic link dynamically in ~/system/cron/cron.10min which links to your 6 | # cron-script instead (which is prefered). Use NAME from 7 | # /data/system/plugindatabase.dat in that case as scriptname! Otherwise the 8 | # cron script will not be uninstalled cleanly. 9 | 10 | # Will be executed as user "loxberry". 11 | -------------------------------------------------------------------------------- /cron/cron.hourly: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is a sample cron file. According to it's name it will go to 4 | # ~/system/cron/cron.hourly. You may also let your Pluginscript create a 5 | # symbolic link dynamically in ~/system/cron/cron.10min which links to your 6 | # cron-script instead (which is prefered). Use NAME from 7 | # /data/system/plugindatabase.dat in that case as scriptname! Otherwise the 8 | # cron script will not be uninstalled cleanly. 9 | 10 | # Will be executed as user "loxberry". 11 | -------------------------------------------------------------------------------- /cron/crontab: -------------------------------------------------------------------------------- 1 | # /etc/cron.d/PLUGINNAME: plugin crontab 2 | # Unlike any other crontab you don't have to run the `crontab' 3 | # command to install the new version when you edit this file 4 | # and files in /etc/cron.d. These files also have username fields, 5 | # that none of the other crontabs do. 6 | 7 | MAILTO="" 8 | SHELL=/bin/sh 9 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 10 | 11 | # m h dom mon dow user command 12 | 0 * * * * loxberry echo "Do something funny" > /dev/null 2>&1 13 | -------------------------------------------------------------------------------- /daemon/daemon: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PLUGINNAME=REPLACELBPPLUGINDIR 4 | LBHOMEDIR=REPLACELBHOMEDIR 5 | 6 | su loxberry -c "/usr/bin/python $LBHOMEDIR/bin/plugins/$PLUGINNAME/frsmsimulator.py -h $LBHOMEDIR > /opt/loxberry/debug1.txt 2>&1&" 7 | #/usr/bin/python $LBHOMEDIR/bin/plugins/$PLUGINNAME/frsmsimulator.py -h $LBHOMEDIR & 8 | 9 | exit 0 -------------------------------------------------------------------------------- /daemon/daemon.bak: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PLUGINNAME=REPLACELBPPLUGINDIR 4 | LBHOMEDIR=REPLACELBHOMEDIR 5 | 6 | sleep 60s 7 | su loxberry -c "/usr/bin/python $LBHOMEDIR/bin/plugins/$PLUGINNAME/frsmsimulator.py -h $LBHOMEDIR > /opt/loxberry/debug1.txt 2>&1&" 8 | #/usr/bin/python $LBHOMEDIR/bin/plugins/$PLUGINNAME/frsmsimulator.py -h $LBHOMEDIR & 9 | 10 | exit 0 -------------------------------------------------------------------------------- /data/sampledata.dat: -------------------------------------------------------------------------------- 1 | # This is a sample data file 2 | -------------------------------------------------------------------------------- /dpkg/.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/dpkg/.swp -------------------------------------------------------------------------------- /dpkg/apt: -------------------------------------------------------------------------------- 1 | # These packages will be installed by apt-get -y install PACKAGENAME 2 | # One line per package, use exact packagename as you would do for apt-get. 3 | # 4 | python3-paho-mqtt 5 | python3-twisted 6 | -------------------------------------------------------------------------------- /dpkg/apt.bak: -------------------------------------------------------------------------------- 1 | # These packages will be installed by apt-get -y install PACKAGENAME 2 | # One line per package, use exact packagename as you would do for apt-get. 3 | # 4 | python3-paho-mqtt 5 | python3-pymodbus 6 | python3-twisted 7 | python-configparser -------------------------------------------------------------------------------- /icons/README.txt: -------------------------------------------------------------------------------- 1 | Please copy your plugin icons here. Please provide icons in PNG fileformat 2 | with transparency and in the following sizes: 3 | 4 | * Size: 64 x 64 5 | * Size: 128 x 128 6 | * Size: 256 x 256 7 | * Size: 512 x 512 8 | 9 | Use the same filenames like the sample files! 10 | 11 | To have a unique Look & Feel you may use one of the standard icons as basis 12 | for your own icon: 13 | 14 | http://download.loxberry.de/development/icons/ -------------------------------------------------------------------------------- /icons/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/icons/icon_128.png -------------------------------------------------------------------------------- /icons/icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/icons/icon_256.png -------------------------------------------------------------------------------- /icons/icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/icons/icon_512.png -------------------------------------------------------------------------------- /icons/icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/icons/icon_64.png -------------------------------------------------------------------------------- /plugin.cfg: -------------------------------------------------------------------------------- 1 | [AUTHOR] 2 | # You can also use a Team/Projekt Name here and a generic email address 3 | # like info@..., BUT NEVER CHANGE this information in future updates! It 4 | # will be used to identify your Plugin, handle updates etc. If you change 5 | # this information, LoxBerry could not identify your plugin and handle it as 6 | # a different one - therefore updates will fail. 7 | NAME=Alexander Asen 8 | EMAIL=alexander.asen@gmail.com 9 | 10 | [PLUGIN] 11 | # The version of your plugin - important if you would like to write an 12 | # upgrade script. Use a correct syntax, which is supported by LoxBerry: 13 | # More info: http://www.loxwiki.eu/x/LYG3AQ 14 | VERSION=0.1.0 15 | 16 | # Short name and prefered subfolder of your Plugin (do not use blanks - they 17 | # will be filtered - and use lowercase only)! Used for script names in 18 | # daemon or cron (NAME) and as unique installation folder. If these names 19 | # already exist on the installation system, we will add 01, 02, 03 and so 20 | # on. Therefore your script should check THESE TWO VARIABLES for figuring 21 | # out subfolder and scriptnames! You will find this information in 22 | # /data/system/plugindatabase.dat after installation! BUT NEVER CHANGE this 23 | # information in future updates! It will be used to identify your Plugin, 24 | # handle updates etc. If you change this information, Loxberry could not 25 | # identify your plugin and will handle it as a different one - therefore 26 | # updates will definetely fail. 27 | NAME=FROSIM_name 28 | FOLDER=frosim_folder 29 | 30 | # Friendly Long Name of your Plugin - 25 Characters maximum! All others maybe 31 | # replaced by "...". You can use blanks, uppercase/lowercase etc. 32 | TITLE=Fronius SmartMeter Simulator 33 | 34 | [AUTOUPDATE] 35 | # If your plugin offers automatic updates, please enable the following option. 36 | # Details here: http://www.loxwiki.eu/x/WoG3AQ 37 | AUTOMATIC_UPDATES=false 38 | 39 | # This is the URL to your release.cfg file. This file will be checked for 40 | # new releases if the user eneables autoupdates. If the version number 41 | # given in the release.cfg file is newer than the installed one, the 42 | # plugin archive will be downloaded from the URL given in the release.cfg file 43 | # and will be installed automatically 44 | RELEASECFG=https://raw.githubusercontent.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/master/release.cfg 45 | 46 | # This is the URL to your prerelease.cfg file. This file will be checked for 47 | # new prereleases if the user enables autoupdates for prereleases. If the 48 | # version number given in the release.cfg file is newer than the installed 49 | # one, the plugin archive will be downloaded from the URL given in the 50 | # release.cfg file and will be installed automatically 51 | PRERELEASECFG=https://raw.githubusercontent.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/master/prerelease.cfg 52 | 53 | [SYSTEM] 54 | # If a reboot is needed after the plugin was installed, enable the following 55 | # option: 56 | REBOOT=false 57 | 58 | # If your Plugin runs only on special LoxBerry Versions, please set the 59 | # following options. If you plugin is compatible with all versions or at least 60 | # with all future versions, please set this to false or leave it empty.. 61 | # Note! If you use the new Plugin Interface V2 - I think you will, because you 62 | # currently read THIS file ;-) - please set the Minimum version to 0.3.0! 63 | LB_MINIMUM=0.3.1 64 | LB_MAXIMUM=false 65 | 66 | # If your plugin runs only on a special architecture (e.g. if you use the GPIOs 67 | # on a Raspberry platform), please set the architecture here. You can seperate 68 | # different architectures with a comma. Set to false or leave it empty if your 69 | # plugin do not use any special features of an architecture. 70 | # PUT in QUOTES ""!!! 71 | # Please check http://www.loxwiki.eu:80/x/VoU_AQ section 5.1 for supported 72 | # architectures and strings you should use here. 73 | ARCHITECTURE="raspberry,x86" 74 | 75 | # If you are using the LoxBerry::Log Modul in PHP or Perl and you would like to use User-defined loglevels, 76 | # enable this option. If enabled, the user will be able to choose a loglevel in the Plugin Management Widget. 77 | CUSTOM_LOGLEVELS=true 78 | 79 | # Plugin Interface. Do not change this if you are not knowing what you are doing! 80 | INTERFACE=2.0 81 | -------------------------------------------------------------------------------- /postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shell script which is executed by bash *AFTER* complete installation is done 4 | # (but *BEFORE* postupdate). Use with caution and remember, that all systems may 5 | # be different! 6 | # 7 | # Exit code must be 0 if executed successfull. 8 | # Exit code 1 gives a warning but continues installation. 9 | # Exit code 2 cancels installation. 10 | # 11 | # Will be executed as user "loxberry". 12 | # 13 | # You can use all vars from /etc/environment in this script. 14 | # 15 | # We add 5 additional arguments when executing this script: 16 | # command 17 | # 18 | # For logging, print to STDOUT. You can use the following tags for showing 19 | # different colorized information during plugin installation: 20 | # 21 | # This was ok!" 22 | # This is just for your information." 23 | # This is a warning!" 24 | # This is an error!" 25 | # This is a fail!" 26 | 27 | # To use important variables from command line use the following code: 28 | COMMAND=$0 # Zero argument is shell command 29 | PTEMPDIR=$1 # First argument is temp folder during install 30 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 31 | PDIR=$3 # Third argument is Plugin installation folder 32 | PVERSION=$4 # Forth argument is Plugin version 33 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 34 | # Base folder of LoxBerry 35 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 36 | 37 | # Combine them with /etc/environment 38 | PCGI=$LBPCGI/$PDIR 39 | PHTML=$LBPHTML/$PDIR 40 | PTEMPL=$LBPTEMPL/$PDIR 41 | PDATA=$LBPDATA/$PDIR 42 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 43 | PCONFIG=$LBPCONFIG/$PDIR 44 | PSBIN=$LBPSBIN/$PDIR 45 | PBIN=$LBPBIN/$PDIR 46 | 47 | echo -n " Current working folder is: " 48 | pwd 49 | echo " Command is: $COMMAND" 50 | echo " Temporary folder is: $PTEMPDIR" 51 | echo " Temporary full path is: $PTEMPPATH" 52 | echo " (Short) Name is: $PSHNAME" 53 | echo " Installation folder is: $PDIR" 54 | echo " Plugin version is: $PVERSION" 55 | echo " Plugin CGI folder is: $PCGI" 56 | echo " Plugin HTML folder is: $PHTML" 57 | echo " Plugin Template folder is: $PTEMPL" 58 | echo " Plugin Data folder is: $PDATA" 59 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 60 | echo " Plugin CONFIG folder is: $PCONFIG" 61 | 62 | # Exit with Status 0 63 | exit 0 64 | -------------------------------------------------------------------------------- /postroot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shell script which is executed by bash *AFTER* complete installation is done 4 | # (*AFTER* postinstall and *AFTER* postupdate). Use with caution and remember, 5 | # that all systems may be different! 6 | # 7 | # Exit code must be 0 if executed successfull. 8 | # Exit code 1 gives a warning but continues installation. 9 | # Exit code 2 cancels installation. 10 | # 11 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | # Will be executed as user "root". 13 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | # 15 | # You can use all vars from /etc/environment in this script. 16 | # 17 | # We add 5 additional arguments when executing this script: 18 | # command 19 | # 20 | # For logging, print to STDOUT. You can use the following tags for showing 21 | # different colorized information during plugin installation: 22 | # 23 | # This was ok!" 24 | # This is just for your information." 25 | # This is a warning!" 26 | # This is an error!" 27 | # This is a fail!" 28 | 29 | # To use important variables from command line use the following code: 30 | COMMAND=$0 # Zero argument is shell command 31 | PTEMPDIR=$1 # First argument is temp folder during install 32 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 33 | PDIR=$3 # Third argument is Plugin installation folder 34 | PVERSION=$4 # Forth argument is Plugin version 35 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 36 | # Base folder of LoxBerry 37 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 38 | 39 | # Combine them with /etc/environment 40 | PCGI=$LBPCGI/$PDIR 41 | PHTML=$LBPHTML/$PDIR 42 | PTEMPL=$LBPTEMPL/$PDIR 43 | PDATA=$LBPDATA/$PDIR 44 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 45 | PCONFIG=$LBPCONFIG/$PDIR 46 | PSBIN=$LBPSBIN/$PDIR 47 | PBIN=$LBPBIN/$PDIR 48 | 49 | echo -n " Current working folder is: " 50 | pwd 51 | echo " Command is: $COMMAND" 52 | echo " Temporary folder is: $PTEMPDIR" 53 | echo " (Short) Name is: $PSHNAME" 54 | echo " Installation folder is: $PDIR" 55 | echo " Plugin version is: $PVERSION" 56 | echo " Plugin CGI folder is: $PCGI" 57 | echo " Plugin HTML folder is: $PHTML" 58 | echo " Plugin Template folder is: $PTEMPL" 59 | echo " Plugin Data folder is: $PDATA" 60 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 61 | echo " Plugin CONFIG folder is: $PCONFIG" 62 | 63 | # Exit with Status 0 64 | exit 0 65 | -------------------------------------------------------------------------------- /postupgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Bash script which is executed in case of an update (if this plugin is already 4 | # installed on the system). This script is executed as very last step (*AFTER* 5 | # postinstall) and can be for example used to save back or convert saved 6 | # userfiles from /tmp back to the system. Use with caution and remember, that 7 | # all systems may be different! 8 | # 9 | # Exit code must be 0 if executed successfull. 10 | # Exit code 1 gives a warning but continues installation. 11 | # Exit code 2 cancels installation. 12 | # 13 | # Will be executed as user "loxberry". 14 | # 15 | # You can use all vars from /etc/environment in this script. 16 | # 17 | # We add 5 additional arguments when executing this script: 18 | # command 19 | # 20 | # For logging, print to STDOUT. You can use the following tags for showing 21 | # different colorized information during plugin installation: 22 | # 23 | # This was ok!" 24 | # This is just for your information." 25 | # This is a warning!" 26 | # This is an error!" 27 | # This is a fail!" 28 | 29 | # To use important variables from command line use the following code: 30 | COMMAND=$0 # Zero argument is shell command 31 | PTEMPDIR=$1 # First argument is temp folder during install 32 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 33 | PDIR=$3 # Third argument is Plugin installation folder 34 | PVERSION=$4 # Forth argument is Plugin version 35 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 36 | # Base folder of LoxBerry 37 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 38 | 39 | # Combine them with /etc/environment 40 | PCGI=$LBPCGI/$PDIR 41 | PHTML=$LBPHTML/$PDIR 42 | PTEMPL=$LBPTEMPL/$PDIR 43 | PDATA=$LBPDATA/$PDIR 44 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 45 | PCONFIG=$LBPCONFIG/$PDIR 46 | PSBIN=$LBPSBIN/$PDIR 47 | PBIN=$LBPBIN/$PDIR 48 | 49 | echo -n " Current working folder is: " 50 | pwd 51 | echo " Command is: $COMMAND" 52 | echo " Temporary folder is: $PTEMPDIR" 53 | echo " (Short) Name is: $PSHNAME" 54 | echo " Installation folder is: $PDIR" 55 | echo " Plugin version is: $PVERSION" 56 | echo " Plugin CGI folder is: $PCGI" 57 | echo " Plugin HTML folder is: $PHTML" 58 | echo " Plugin Template folder is: $PTEMPL" 59 | echo " Plugin Data folder is: $PDATA" 60 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 61 | echo " Plugin CONFIG folder is: $PCONFIG" 62 | 63 | exit 0 64 | -------------------------------------------------------------------------------- /preinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shell script which is executed by bash *BEFORE* installation is started (but 4 | # *AFTER* preupdate). Use with caution and remember, that all systems may be 5 | # different! 6 | # 7 | # Exit code must be 0 if executed successfull. 8 | # Exit code 1 gives a warning but continues installation. 9 | # Exit code 2 cancels installation. 10 | # 11 | # Will be executed as user "loxberry". 12 | # 13 | # You can use all vars from /etc/environment in this script. 14 | # 15 | # We add 5 additional arguments when executing this script: 16 | # command 17 | # 18 | # For logging, print to STDOUT. You can use the following tags for showing 19 | # different colorized information during plugin installation: 20 | # 21 | # This was ok!" 22 | # This is just for your information." 23 | # This is a warning!" 24 | # This is an error!" 25 | # This is a fail!" 26 | 27 | # To use important variables from command line use the following code: 28 | COMMAND=$0 # Zero argument is shell command 29 | PTEMPDIR=$1 # First argument is temp folder during install 30 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 31 | PDIR=$3 # Third argument is Plugin installation folder 32 | PVERSION=$4 # Forth argument is Plugin version 33 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 34 | # Base folder of LoxBerry 35 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 36 | 37 | # Combine them with /etc/environment 38 | PCGI=$LBPCGI/$PDIR 39 | PHTML=$LBPHTML/$PDIR 40 | PTEMPL=$LBPTEMPL/$PDIR 41 | PDATA=$LBPDATA/$PDIR 42 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 43 | PCONFIG=$LBPCONFIG/$PDIR 44 | PSBIN=$LBPSBIN/$PDIR 45 | PBIN=$LBPBIN/$PDIR 46 | 47 | echo -n " Current working folder is: " 48 | pwd 49 | echo " Command is: $COMMAND" 50 | echo " Temporary folder is: $PTEMPDIR" 51 | echo " (Short) Name is: $PSHNAME" 52 | echo " Installation folder is: $PDIR" 53 | echo " Plugin version is: $PVERSION" 54 | echo " Plugin CGI folder is: $PCGI" 55 | echo " Plugin HTML folder is: $PHTML" 56 | echo " Plugin Template folder is: $PTEMPL" 57 | echo " Plugin Data folder is: $PDATA" 58 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 59 | echo " Plugin CONFIG folder is: $PCONFIG" 60 | 61 | exit 0 62 | -------------------------------------------------------------------------------- /prerelease.cfg: -------------------------------------------------------------------------------- 1 | [AUTOUPDATE] 2 | # This file is used if you would like to provide automatic updates. Put 3 | # this file into your repo and give a link to this file in your plugin.cfg 4 | # If you would like to release an update, edit VERSION here and give 5 | # the donwload link here 6 | 7 | 8 | # Version of the new release 9 | VERSION=1.0.3 10 | 11 | # Download URL of the ZIP Archive 12 | ARCHIVEURL=https://github.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/archive/prerelease_1.0.2.zip 13 | 14 | # URL for further information about this release 15 | INFOURL=https://github.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/commits 16 | -------------------------------------------------------------------------------- /preroot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shell script which is executed by bash *BEFORE* installation is started 4 | # (*BEFORE* preinstall and *BEFORE* preupdate). Use with caution and remember, 5 | # that all systems may be different! 6 | # 7 | # Exit code must be 0 if executed successfull. 8 | # Exit code 1 gives a warning but continues installation. 9 | # Exit code 2 cancels installation. 10 | # 11 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | # Will be executed as user "root". 13 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | # 15 | # You can use all vars from /etc/environment in this script. 16 | # 17 | # We add 5 additional arguments when executing this script: 18 | # command 19 | # 20 | # For logging, print to STDOUT. You can use the following tags for showing 21 | # different colorized information during plugin installation: 22 | # 23 | # This was ok!" 24 | # This is just for your information." 25 | # This is a warning!" 26 | # This is an error!" 27 | # This is a fail!" 28 | 29 | # To use important variables from command line use the following code: 30 | COMMAND=$0 # Zero argument is shell command 31 | PTEMPDIR=$1 # First argument is temp folder during install 32 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 33 | PDIR=$3 # Third argument is Plugin installation folder 34 | PVERSION=$4 # Forth argument is Plugin version 35 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 36 | # Base folder of LoxBerry 37 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 38 | 39 | # Combine them with /etc/environment 40 | PCGI=$LBPCGI/$PDIR 41 | PHTML=$LBPHTML/$PDIR 42 | PTEMPL=$LBPTEMPL/$PDIR 43 | PDATA=$LBPDATA/$PDIR 44 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 45 | PCONFIG=$LBPCONFIG/$PDIR 46 | PSBIN=$LBPSBIN/$PDIR 47 | PBIN=$LBPBIN/$PDIR 48 | 49 | echo -n " Current working folder is: " 50 | pwd 51 | echo " Command is: $COMMAND" 52 | echo " Temporary folder is: $PTEMPDIR" 53 | echo " (Short) Name is: $PSHNAME" 54 | echo " Installation folder is: $PDIR" 55 | echo " Plugin version is: $PVERSION" 56 | echo " Plugin CGI folder is: $PCGI" 57 | echo " Plugin HTML folder is: $PHTML" 58 | echo " Plugin Template folder is: $PTEMPL" 59 | echo " Plugin Data folder is: $PDATA" 60 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 61 | echo " Plugin CONFIG folder is: $PCONFIG" 62 | 63 | python -m pip install pymodbus 64 | 65 | exit 0 66 | -------------------------------------------------------------------------------- /preupgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Shell script which is executed in case of an update (if this plugin is already 4 | # installed on the system). This script is executed as very first step (*BEFORE* 5 | # preinstall.sh) and can be used e.g. to save existing configfiles to /tmp 6 | # during installation. Use with caution and remember, that all systems may be 7 | # different! 8 | # 9 | # Exit code must be 0 if executed successfull. 10 | # Exit code 1 gives a warning but continues installation. 11 | # Exit code 2 cancels installation. 12 | # 13 | # Will be executed as user "loxberry". 14 | # 15 | # You can use all vars from /etc/environment in this script. 16 | # 17 | # We add 5 additional arguments when executing this script: 18 | # command 19 | # 20 | # For logging, print to STDOUT. You can use the following tags for showing 21 | # different colorized information during plugin installation: 22 | # 23 | # This was ok!" 24 | # This is just for your information." 25 | # This is a warning!" 26 | # This is an error!" 27 | # This is a fail!" 28 | 29 | # To use important variables from command line use the following code: 30 | COMMAND=$0 # Zero argument is shell command 31 | PTEMPDIR=$1 # First argument is temp folder during install 32 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 33 | PDIR=$3 # Third argument is Plugin installation folder 34 | PVERSION=$4 # Forth argument is Plugin version 35 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 36 | # Base folder of LoxBerry 37 | PTEMPPATH=$6 # Sixth argument is full temp path during install (see also $1) 38 | 39 | # Combine them with /etc/environment 40 | PCGI=$LBPCGI/$PDIR 41 | PHTML=$LBPHTML/$PDIR 42 | PTEMPL=$LBPTEMPL/$PDIR 43 | PDATA=$LBPDATA/$PDIR 44 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 45 | PCONFIG=$LBPCONFIG/$PDIR 46 | PSBIN=$LBPSBIN/$PDIR 47 | PBIN=$LBPBIN/$PDIR 48 | 49 | echo -n " Current working folder is: " 50 | pwd 51 | echo " Command is: $COMMAND" 52 | echo " Temporary folder is: $PTEMPDIR" 53 | echo " (Short) Name is: $PSHNAME" 54 | echo " Installation folder is: $PDIR" 55 | echo " Plugin version is: $PVERSION" 56 | echo " Plugin CGI folder is: $PCGI" 57 | echo " Plugin HTML folder is: $PHTML" 58 | echo " Plugin Template folder is: $PTEMPL" 59 | echo " Plugin Data folder is: $PDATA" 60 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 61 | echo " Plugin CONFIG folder is: $PCONFIG" 62 | 63 | exit 0 64 | -------------------------------------------------------------------------------- /release.cfg: -------------------------------------------------------------------------------- 1 | [AUTOUPDATE] 2 | # This file is used if you would like to provide automatic updates. Put 3 | # this file into your repo and give a link to this file in your plugin.cfg 4 | # If you would like to release an update, edit VERSION here and give 5 | # the donwload link here 6 | 7 | # Version of the new release 8 | VERSION=1.0.3 9 | 10 | # Download URL of the ZIP Archive 11 | ARCHIVEURL=https://github.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/archive/release_1.0.1.zip 12 | 13 | # URL for further information about this release 14 | INFOURL=https://github.com/mschlenstedt/LoxBerry-Plugin-SamplePlugin-V2-Perl/commits 15 | -------------------------------------------------------------------------------- /sudoers/sudoers: -------------------------------------------------------------------------------- 1 | # Sudoers file for the plugin. Note: All scripts in sbin will be added automatically 2 | %loxberry ALL = NOPASSWD: REPLACELBPBINDIR/hereiswhatwehavetodoasroot 3 | -------------------------------------------------------------------------------- /templates/help/help.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | 7 |

8 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 62 | 63 | 84 | 85 | 86 | 87 |
Fronius SmartMeter Simulator
88 | 89 | 90 |

91 |

92 |

93 |
94 | 95 | 96 | 97 |
98 |
99 |
100 | 101 |         102 | 104 |
105 |
106 |
107 | 108 | 109 | 110 | 111 | 114 | 117 | 120 | 121 | 122 | 123 | 126 | 129 | 132 | 133 | 134 | 137 | 140 | 143 | 144 | 145 | 148 | 151 | 154 | 155 | 156 | 159 | 162 | 165 | 166 | 167 | 168 | 171 | 181 | 184 | 187 | 188 |
112 | 113 | 115 | 116 | 118 |   119 |
124 | 125 | 127 | 128 | 130 |   131 |
135 | 136 | 138 | 139 | 141 |   142 |
146 | 147 | 149 | 150 | 152 |   153 |
157 | 158 | 160 | 161 | 163 |   164 |
169 |   170 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
 
179 | 180 |
182 |   183 | 185 | 186 |
189 | 190 |
191 |
192 | Service Status: 193 |
194 |
195 | 196 |
197 |
198 | Refresh 199 |
200 | 201 |
202 | 203 |

204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /templates/index.html.bak: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Fronius SmartMeter Simulator
4 | 5 | 6 |

7 |

8 |

9 |
10 | 11 | 12 | 13 | 14 | 15 | 18 | 21 | 24 | 25 | 26 | 27 | 30 | 33 | 36 | 37 | 38 | 41 | 44 | 47 | 48 | 49 | 52 | 55 | 58 | 59 | 60 | 63 | 66 | 69 | 70 | 71 | 72 | 75 | 84 | 87 | 90 | 91 | 92 | 93 | 96 | 99 | 100 |
16 | 17 | 19 | 20 | 22 |   23 |
28 | 29 | 31 | 32 | 34 |   35 |
39 | 40 | 42 | 43 | 45 |   46 |
50 | 51 | 53 | 54 | 56 |   57 |
61 | 62 | 64 | 65 | 67 |   68 |
73 |   74 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
 
83 |
85 |   86 | 88 | 89 |
94 |   95 | 97 | 98 |
101 | 102 | 103 | 104 |

-------------------------------------------------------------------------------- /templates/lang/help_de.ini: -------------------------------------------------------------------------------- 1 | 2 | [HELP] 3 | INTRO="Das ist ein Beispiel-Hilfedatei basierend auf einem Template. Die Hilfe kann auch mit dem Translate Widget von LoxBerry übersetzt werden." 4 | LINK_INFO="Es ist auch möglich, Links in der Sprachdatei zu benutzen. Wenn du die Link-URL als Sprach-Phrase definierst, kannst du sprachspezifisch zu unterschiedlichen Webseiten verweisen. Das ist ein Link auf die LoxBerry Wiki Startseite:" 5 | LINK="http://www.loxwiki.eu/x/o4CO" 6 | LINK_TEXT="LoxBerry Wiki (deutsch)" 7 | -------------------------------------------------------------------------------- /templates/lang/help_en.ini: -------------------------------------------------------------------------------- 1 | [HELP] 2 | INTRO="This is a sample help file based on a template. It also can be translated with the Translate widget to other languages." 3 | LINK_INFO="It is also possible to write links to your translation files. If you define a hyperlink url as a language phrase, you can provide language specific links to different websites. This is a link to the LoxBerry Wiki langing page:" 4 | LINK="http://www.loxwiki.eu/x/cYSdAQ" 5 | LINK_TEXT="LoxBerry Wiki (english)" 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /templates/lang/language_de.ini: -------------------------------------------------------------------------------- 1 | [BASIC] 2 | FROSIM_GENERAL_DESCRIPTION="Diese Plugin simuliert einen Fronius Smartmeter. Es werden die notwendigen Messwerte via MQTT empfangen und per MODBUS RTU dem Fronius Wechselrichter zur Verfügung gestellt." 3 | FROSIM_CORR_DESCRIPTION="Fronius Datamanager benötigt zur Berechnung des aktuellen Verbrauchs für die Statistik die absoluten Zählerwerte von Bezug und Einspeisung in Wattstunden (Wh). Werden die verfügbaren Daten jedoch z.B.: in kWh bereitgestellt, so müssen diese in Wattstunden umgewandelt werden. Dafür ist der Korrekturfaktor einzugeben. Der Wert ist 0 wenn die MQTT-Daten bereits in Wattstunden vorliegen und 1000 wenn die Daten in kWh übertragen werden." 4 | FROSIM_WARNING="Warnung: Sollte bei der Eingabe des Umrechnungsfaktors ein Fehler passieren könnte die gesamte Statstik im Datamanger gestört werden. Ein Löschen von Daten aus der Statistik kann, wenn überhaupt ausschließlich Fronius durchführen!" 5 | 6 | [CONFIGURATION] 7 | lblMQTTTopicConsumption="MQTT Topic aktueller Verbrauch:" 8 | lblMQTTTopicImport="MQTT Topic Bezug (Absolutwert):" 9 | lblMQTTTopicExport="MQTT Topic Einspeisung (Absolutwert):" 10 | lblCorrectionFactor="Korrekturfaktor:" 11 | lblSerialPort="Port des Modbus Adapters (z.B. /dev/ttyUSB0):" 12 | btnSave="Konfiguration speichern" 13 | btnReStart="Smart Meter Simulator (Neu)start" 14 | btnStop="Smart Meter Simulator stoppen" 15 | serviceUnknown="Unbekannt" 16 | serviceRunning="Running" 17 | serviceStopped="Stopped" 18 | -------------------------------------------------------------------------------- /templates/lang/language_en.ini: -------------------------------------------------------------------------------- 1 | [BASIC] 2 | HEADING_BASICSETTINGS="Basic Settings" 3 | WELCOME="Welcome to LoxBerry!" 4 | SAMPLEPLUGIN_HINT="This is just a sample plugin." 5 | SAMPLEPLUGIN_BESTPRACTISE="It should show you a best practise - how things could be done in a smart way if you create a plugin." 6 | IS_ENABLED="Is enabled" 7 | IS_DISABLED="Is disabled" 8 | 9 | [FOOTER] 10 | GREETING="Kind regards, The LoxBerry team." 11 | -------------------------------------------------------------------------------- /templates/sample.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 |

7 | 8 |
9 | 10 | Now a sample form with two select lists. We created these lists 11 | in two different ways. The result here is the very same. 12 | 13 |
14 | 15 |

First way: Use LOOP in the template

16 | 17 |
18 | 25 |
26 | 27 |
28 | 29 |

Second way: Use a pre-created selectlist in the template

30 | 31 |
32 | 35 |
36 | 37 |
38 | 39 |

And now some siple variables

40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |

48 | 49 | 50 | -------------------------------------------------------------------------------- /uninstall/uninstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bashscript which is executed by bash when uninstalling the plugin 4 | # Use with caution and remember, that all systems may be different! 5 | # 6 | # Exit code must be 0 if executed successfull. 7 | # Exit code 1 gives a warning but continues deinstallation. 8 | # 9 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | # Will be executed as user "root". 11 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | # 13 | # You can use all vars from /etc/environment in this script. 14 | # 15 | # We add 5 additional arguments when executing this script: 16 | # command 17 | # 18 | # For logging, print to STDOUT. You can use the following tags for showing 19 | # different colorized information during plugin installation: 20 | # 21 | # This was ok!" 22 | # This is just for your information." 23 | # This is a warning!" 24 | # This is an error!" 25 | # This is a fail!" 26 | 27 | # To use important variables from command line use the following code: 28 | COMMAND=$0 # Zero argument is shell command 29 | PTEMPDIR=$1 # First argument is temp folder during install 30 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 31 | PDIR=$3 # Third argument is Plugin installation folder 32 | PVERSION=$4 # Forth argument is Plugin version 33 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 34 | # Base folder of LoxBerry 35 | 36 | # Combine them with /etc/environment 37 | PCGI=$LBPCGI/$PDIR 38 | PHTML=$LBPHTML/$PDIR 39 | PTEMPL=$LBPTEMPL/$PDIR 40 | PDATA=$LBPDATA/$PDIR 41 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 42 | PCONFIG=$LBPCONFIG/$PDIR 43 | PSBIN=$LBPSBIN/$PDIR 44 | PBIN=$LBPBIN/$PDIR 45 | 46 | echo " Command is: $COMMAND" 47 | echo " Temporary folder is: $TEMPDIR" 48 | echo " (Short) Name is: $PSHNAME" 49 | echo " Installation folder is: $ARGV3" 50 | echo " Plugin version is: $ARGV4" 51 | echo " Plugin CGI folder is: $PCGI" 52 | echo " Plugin HTML folder is: $PHTML" 53 | echo " Plugin Template folder is: $PTEMPL" 54 | echo " Plugin Data folder is: $PDATA" 55 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 56 | echo " Plugin CONFIG folder is: $PCONFIG" 57 | echo " Plugin SBIN folder is: $PSBIN" 58 | echo " Plugin BIN folder is: $PBIN" 59 | 60 | # Exit with Status 0 61 | exit 0 62 | -------------------------------------------------------------------------------- /uninstall/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bashscript which is executed by bash when uninstalling the plugin 4 | # Use with caution and remember, that all systems may be different! 5 | # 6 | # Exit code must be 0 if executed successfull. 7 | # Exit code 1 gives a warning but continues deinstallation. 8 | # 9 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | # Will be executed as user "root". 11 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 12 | # 13 | # You can use all vars from /etc/environment in this script. 14 | # 15 | # We add 5 additional arguments when executing this script: 16 | # command 17 | # 18 | # For logging, print to STDOUT. You can use the following tags for showing 19 | # different colorized information during plugin installation: 20 | # 21 | # This was ok!" 22 | # This is just for your information." 23 | # This is a warning!" 24 | # This is an error!" 25 | # This is a fail!" 26 | 27 | # To use important variables from command line use the following code: 28 | COMMAND=$0 # Zero argument is shell command 29 | PTEMPDIR=$1 # First argument is temp folder during install 30 | PSHNAME=$2 # Second argument is Plugin-Name for scipts etc. 31 | PDIR=$3 # Third argument is Plugin installation folder 32 | PVERSION=$4 # Forth argument is Plugin version 33 | #LBHOMEDIR=$5 # Comes from /etc/environment now. Fifth argument is 34 | # Base folder of LoxBerry 35 | 36 | # Combine them with /etc/environment 37 | PCGI=$LBPCGI/$PDIR 38 | PHTML=$LBPHTML/$PDIR 39 | PTEMPL=$LBPTEMPL/$PDIR 40 | PDATA=$LBPDATA/$PDIR 41 | PLOG=$LBPLOG/$PDIR # Note! This is stored on a Ramdisk now! 42 | PCONFIG=$LBPCONFIG/$PDIR 43 | PSBIN=$LBPSBIN/$PDIR 44 | PBIN=$LBPBIN/$PDIR 45 | 46 | echo " Command is: $COMMAND" 47 | echo " Temporary folder is: $TEMPDIR" 48 | echo " (Short) Name is: $PSHNAME" 49 | echo " Installation folder is: $ARGV3" 50 | echo " Plugin version is: $ARGV4" 51 | echo " Plugin CGI folder is: $PCGI" 52 | echo " Plugin HTML folder is: $PHTML" 53 | echo " Plugin Template folder is: $PTEMPL" 54 | echo " Plugin Data folder is: $PDATA" 55 | echo " Plugin Log folder (on RAMDISK!) is: $PLOG" 56 | echo " Plugin CONFIG folder is: $PCONFIG" 57 | echo " Plugin SBIN folder is: $PSBIN" 58 | echo " Plugin BIN folder is: $PBIN" 59 | 60 | # Exit with Status 0 61 | exit 0 62 | -------------------------------------------------------------------------------- /webfrontend/html/.dummy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/americanium/fronius_sm_simulator_rtu/85149aae8cfad79979131bb2bb5d4075ffc60bfc/webfrontend/html/.dummy -------------------------------------------------------------------------------- /webfrontend/html/index.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | print "Content-type: text/html\n\n"; 4 | 5 | print "Hello, world in Perl!\n"; 6 | -------------------------------------------------------------------------------- /webfrontend/htmlauth/index.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Einbinden der LoxBerry-Module 4 | use CGI; 5 | use LoxBerry::System; 6 | use LoxBerry::Web; 7 | use LoxBerry::JSON; 8 | use LoxBerry::Log; 9 | 10 | # Die Version des Plugins wird direkt aus der Plugin-Datenbank gelesen. 11 | my $version = LoxBerry::System::pluginversion(); 12 | 13 | # Mit dieser Konstruktion lesen wir uns alle POST-Parameter in den Namespace R. 14 | my $cgi = CGI->new; 15 | $cgi->import_names('R'); 16 | # Ab jetzt kann beispielsweise ein POST-Parameter 'form' ausgelesen werden mit $R::form. 17 | 18 | 19 | # Wir holen uns die Plugin-Config in den Hash %pcfg. Damit kannst du die Parameter mit $pcfg{'Section.Label'} direkt auslesen. 20 | #my %pcfg; 21 | #tie %pcfg, "Config::Simple", "$lbpconfigdir/pluginconfig.cfg"; 22 | my $pcfg = new Config::Simple("$lbpconfigdir/config.cfg"); 23 | 24 | # ---------------------------------- 25 | #our %navbar; 26 | #$navbar{1}{Name} = "First Menu"; 27 | #$navbar{1}{URL} = 'index.cgi'; 28 | 29 | #$navbar{2}{Name} = "Second Menu"; 30 | #$navbar{2}{URL} = 'second.cgi'; 31 | #$navbar{2}{active} = 1; 32 | 33 | #$navbar{3}{Name} = "External Website"; 34 | #$navbar{3}{URL} = 'http://www.loxberry.de'; 35 | #$navbar{3}{target} = '_blank'; 36 | 37 | #LoxBerry::Web::lbheader($plugintitle, $helplink, $helptemplate); 38 | 39 | # ----------------------------- 40 | 41 | if ( $R::ajax ) { 42 | 43 | print "Content-type: application/json; charset=utf-8\r\n\r\n"; 44 | 45 | # Get pids 46 | if( $R::ajax eq "getpids" ) { 47 | my $pid = trim(`pgrep -f frsmsimulator.py`); 48 | print to_json( { response => $pid } ); 49 | } 50 | 51 | # Service Restart 52 | if( $R::ajax eq "servicerestart" ) { 53 | system("pkill -f frsmsimulator.py > /dev/null 2>&1"); 54 | sleep (1); 55 | system("python $lbpbindir/frsmsimulator.py -h $lbhomedir > /dev/null 2>&1 &"); 56 | my $pid = trim(`pgrep -f frsmsimulator.py`); 57 | print to_json( { response => $pid } ); 58 | } 59 | 60 | exit; 61 | 62 | } else { 63 | 64 | # Wir initialisieren unser Template. Der Pfad zum Templateverzeichnis steht in der globalen Variable $lbptemplatedir. 65 | my $template = HTML::Template->new( 66 | filename => "$lbptemplatedir/index.html", 67 | global_vars => 1, 68 | loop_context_vars => 1, 69 | die_on_bad_params => 0, 70 | associate => $cgi, 71 | ); 72 | 73 | # Jetzt lassen wir uns die Sprachphrasen lesen. Ohne Pfadangabe wird im Ordner lang nach language_de.ini, language_en.ini usw. gesucht. 74 | # Wir kümmern uns im Code nicht weiter darum, welche Sprache nun zu lesen wäre. 75 | # Mit der Routine wird die Sprache direkt ins Template übernommen. Sollten wir trotzdem im Code eine brauchen, bekommen 76 | # wir auch noch einen Hash zurück. 77 | my %L = LoxBerry::Web::readlanguage($template, "language.ini"); 78 | 79 | 80 | # Wir Übergeben die Titelzeile (mit Versionsnummer), einen Link ins Wiki und das Hilfe-Template. 81 | # Um die Sprache der Hilfe brauchen wir uns im Code nicht weiter zu kümmern. 82 | LoxBerry::Web::lbheader("Sample Plugin for Perl V$version", "http://www.loxwiki.eu/x/2wN7AQ", "help.html"); 83 | 84 | 85 | # --------------------------------------------------- 86 | # Save settings to config file 87 | # --------------------------------------------------- 88 | if ($R::btnSave) 89 | { 90 | $pcfg->param('CONFIGURATION.TOPIC_CONSUMPTION', $R::txtMQTTTopicConsumption); 91 | $pcfg->param('CONFIGURATION.TOPIC_TOTAL_IMPORT', $R::txtMQTTTopicExport); 92 | $pcfg->param('CONFIGURATION.TOPIC_TOTAL_EXPORT', $R::txtMQTTTopicImport); 93 | $pcfg->param('CONFIGURATION.CORRFACTOR', $R::txtCorrectionFactor); 94 | $pcfg->param('CONFIGURATION.SERIAL_PORT', $R::txtSerialPort); 95 | 96 | $pcfg->save(); 97 | } 98 | 99 | 100 | if ($R::btnReStart) 101 | { 102 | 103 | system("pkill -f frsmsimulator"); 104 | system("python $lbpbindir/frsmsimulator.py -h $lbhomedir &"); 105 | #&state; 106 | 107 | 108 | } 109 | 110 | 111 | if ($R::btnStop) 112 | { 113 | 114 | system("pkill -f frsmsimulator"); 115 | #&state; 116 | 117 | } 118 | 119 | # --------------------------------------------------- 120 | # Control for "frmStart" Form 121 | # --------------------------------------------------- 122 | my $frmStart = $cgi->start_form( 123 | -name => 'FrosimPlugIn', 124 | -method => 'POST', 125 | ); 126 | $template->param( frmStart => $frmStart ); 127 | 128 | 129 | # --------------------------------------------------- 130 | # Control for "frmEnd" Form 131 | # --------------------------------------------------- 132 | my $frmEnd = $cgi->end_form(); 133 | $template->param( frmEnd => $frmEnd ); 134 | 135 | 136 | 137 | 138 | # --------------------------------------------------- 139 | # Control for "txtMQTTTopicConsumption" Textfield 140 | # --------------------------------------------------- 141 | my $txtMQTTTopicConsumption = $cgi->textfield( 142 | -name => 'txtMQTTTopicConsumption', 143 | -default => $pcfg->param('CONFIGURATION.TOPIC_CONSUMPTION'), 144 | ); 145 | $template->param( txtMQTTTopicConsumption => $txtMQTTTopicConsumption ); 146 | 147 | # --------------------------------------------------- 148 | # Control for "txtMQTTTopicImport" Textfield 149 | # --------------------------------------------------- 150 | my $txtMQTTTopicImport = $cgi->textfield( 151 | -name => 'txtMQTTTopicImport', 152 | -default => $pcfg->param('CONFIGURATION.TOPIC_TOTAL_IMPORT'), 153 | ); 154 | $template->param( txtMQTTTopicImport => $txtMQTTTopicImport ); 155 | 156 | # --------------------------------------------------- 157 | # Control for "txtMQTTTopicExport" Textfield 158 | # --------------------------------------------------- 159 | my $txtMQTTTopicExport = $cgi->textfield( 160 | -name => 'txtMQTTTopicExport', 161 | -default => $pcfg->param('CONFIGURATION.TOPIC_TOTAL_EXPORT'), 162 | ); 163 | $template->param( txtMQTTTopicExport => $txtMQTTTopicExport ); 164 | 165 | # --------------------------------------------------- 166 | # Control for "txtCorrectionFactor" Textfield 167 | # --------------------------------------------------- 168 | my $txtCorrectionFactor = $cgi->textfield( 169 | -name => 'txtCorrectionFactor', 170 | -default => $pcfg->param('CONFIGURATION.CORRFACTOR'), 171 | ); 172 | $template->param( txtCorrectionFactor => $txtCorrectionFactor ); 173 | 174 | # --------------------------------------------------- 175 | # Control for "txtSerialPort" Textfield 176 | # --------------------------------------------------- 177 | my $txtSerialPort = $cgi->textfield( 178 | -name => 'txtSerialPort', 179 | -default => $pcfg->param('CONFIGURATION.SERIAL_PORT'), 180 | ); 181 | $template->param( txtSerialPort => $txtSerialPort ); 182 | 183 | # --------------------------------------------------- 184 | # Control for "btnSave" Button 185 | # --------------------------------------------------- 186 | my $btnSave = $cgi->submit( 187 | -name => 'btnSave', 188 | -value => $L{'CONFIGURATION.btnSave'}, 189 | ); 190 | $template->param( btnSave => $btnSave ); 191 | 192 | # --------------------------------------------------- 193 | # Control for "btnReStart" Button 194 | # --------------------------------------------------- 195 | my $btnReStart = $cgi->submit( 196 | -name => 'btnReStart', 197 | -value => $L{'CONFIGURATION.btnReStart'}, 198 | ); 199 | $template->param( btnReStart => $btnReStart); 200 | 201 | 202 | # --------------------------------------------------- 203 | # Control for "btnStop" Button 204 | # --------------------------------------------------- 205 | my $btnStop = $cgi->submit( 206 | -name => 'btnStop', 207 | -value => $L{'CONFIGURATION.btnStop'}, 208 | ); 209 | $template->param( btnStop => $btnStop); 210 | 211 | # Den so erzeugten HTML-Code schreiben wir ins Template. 212 | 213 | $template->param( ACTIVATED => $activated); 214 | 215 | # --------------------------------------------------- 216 | # Evaluating status and PID of script runnning 217 | # --------------------------------------------------- 218 | 219 | #sub state 220 | #{ 221 | 222 | my ($exitcode, $pid) = execute("pgrep -f frsmsimulator.py"); 223 | 224 | if ($pid == 0) 225 | { 226 | $status = "Service stopped"; 227 | 228 | }elsif ($pid != 0) 229 | { 230 | 231 | $status = "Service running"; 232 | } 233 | 234 | $template->param( PID => $pid); 235 | $template->param( STATUS => $status); 236 | #} 237 | 238 | # --------------------------------------------------- 239 | # Localized Labels from language.ini 240 | # --------------------------------------------------- 241 | 242 | $template->param( lblMQTTTopicConsumption => $L{'CONFIGURATION.lblMQTTTopicConsumption'} ); 243 | $template->param( lblMQTTTopicImport => $L{'CONFIGURATION.lblMQTTTopicImport'} ); 244 | $template->param( lblMQTTTopicExport => $L{'CONFIGURATION.lblMQTTTopicExport'} ); 245 | $template->param( lblCorrectionFactor => $L{'CONFIGURATION.lblCorrectionFactor'} ); 246 | $template->param( lblSerialPort => $L{'CONFIGURATION.lblSerialPort'} ); 247 | 248 | 249 | # Nun wird das Template ausgegeben. 250 | print $template->output(); 251 | 252 | 253 | # Schlussendlich lassen wir noch den Footer ausgeben. 254 | LoxBerry::Web::lbfooter(); 255 | } -------------------------------------------------------------------------------- /webfrontend/htmlauth/index.cgi.bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Einbinden der LoxBerry-Module 4 | use CGI; 5 | use LoxBerry::System; 6 | use LoxBerry::Web; 7 | 8 | # Die Version des Plugins wird direkt aus der Plugin-Datenbank gelesen. 9 | my $version = LoxBerry::System::pluginversion(); 10 | 11 | # Mit dieser Konstruktion lesen wir uns alle POST-Parameter in den Namespace R. 12 | my $cgi = CGI->new; 13 | $cgi->import_names('R'); 14 | # Ab jetzt kann beispielsweise ein POST-Parameter 'form' ausgelesen werden mit $R::form. 15 | 16 | 17 | # Wir Übergeben die Titelzeile (mit Versionsnummer), einen Link ins Wiki und das Hilfe-Template. 18 | # Um die Sprache der Hilfe brauchen wir uns im Code nicht weiter zu kümmern. 19 | LoxBerry::Web::lbheader("Sample Plugin for Perl V$version", "http://www.loxwiki.eu/x/2wN7AQ", "help.html"); 20 | 21 | # Wir holen uns die Plugin-Config in den Hash %pcfg. Damit kannst du die Parameter mit $pcfg{'Section.Label'} direkt auslesen. 22 | #my %pcfg; 23 | #tie %pcfg, "Config::Simple", "$lbpconfigdir/pluginconfig.cfg"; 24 | my $pcfg = new Config::Simple("$lbpconfigdir/config.cfg"); 25 | 26 | 27 | # Wir initialisieren unser Template. Der Pfad zum Templateverzeichnis steht in der globalen Variable $lbptemplatedir. 28 | 29 | my $template = HTML::Template->new( 30 | filename => "$lbptemplatedir/index.html", 31 | global_vars => 1, 32 | loop_context_vars => 1, 33 | die_on_bad_params => 0, 34 | associate => $cgi, 35 | ); 36 | 37 | # Jetzt lassen wir uns die Sprachphrasen lesen. Ohne Pfadangabe wird im Ordner lang nach language_de.ini, language_en.ini usw. gesucht. 38 | # Wir kümmern uns im Code nicht weiter darum, welche Sprache nun zu lesen wäre. 39 | # Mit der Routine wird die Sprache direkt ins Template übernommen. Sollten wir trotzdem im Code eine brauchen, bekommen 40 | # wir auch noch einen Hash zurück. 41 | my %L = LoxBerry::Web::readlanguage($template, "language.ini"); 42 | 43 | my $lastdata; 44 | 45 | # --------------------------------------------------- 46 | # Save settings to config file 47 | # --------------------------------------------------- 48 | if ($R::btnSave) 49 | { 50 | $pcfg->param('CONFIGURATION.TOPIC_CONSUMPTION', $R::txtMQTTTopicConsumption); 51 | $pcfg->param('CONFIGURATION.TOPIC_TOTAL_IMPORT', $R::txtMQTTTopicExport); 52 | $pcfg->param('CONFIGURATION.TOPIC_TOTAL_EXPORT', $R::txtMQTTTopicImport); 53 | $pcfg->param('CONFIGURATION.CORRFACTOR', $R::txtCorrectionFactor); 54 | $pcfg->param('CONFIGURATION.SERIAL_PORT', $R::txtSerialPort); 55 | 56 | $pcfg->save(); 57 | } 58 | 59 | if ($R::btnStart) 60 | { 61 | 62 | system("/usr/bin/python $LBHOMEDIR/bin/plugins/$PLUGINNAME/frsmsimulator.py -h $LBHOMEDIR &"); 63 | 64 | } 65 | 66 | if ($R::btnStop) 67 | { 68 | 69 | system("pkill -f frsmsimulator"); 70 | 71 | } 72 | 73 | # --------------------------------------------------- 74 | # Control for "frmStart" Form 75 | # --------------------------------------------------- 76 | my $frmStart = $cgi->start_form( 77 | -name => 'FrosimPlugIn', 78 | -method => 'POST', 79 | ); 80 | $template->param( frmStart => $frmStart ); 81 | 82 | 83 | # --------------------------------------------------- 84 | # Control for "frmEnd" Form 85 | # --------------------------------------------------- 86 | my $frmEnd = $cgi->end_form(); 87 | $template->param( frmEnd => $frmEnd ); 88 | 89 | 90 | 91 | 92 | # --------------------------------------------------- 93 | # Control for "txtMQTTTopicConsumption" Textfield 94 | # --------------------------------------------------- 95 | my $txtMQTTTopicConsumption = $cgi->textfield( 96 | -name => 'txtMQTTTopicConsumption', 97 | -default => $pcfg->param('CONFIGURATION.TOPIC_CONSUMPTION'), 98 | ); 99 | $template->param( txtMQTTTopicConsumption => $txtMQTTTopicConsumption ); 100 | 101 | # --------------------------------------------------- 102 | # Control for "txtMQTTTopicImport" Textfield 103 | # --------------------------------------------------- 104 | my $txtMQTTTopicImport = $cgi->textfield( 105 | -name => 'txtMQTTTopicImport', 106 | -default => $pcfg->param('CONFIGURATION.TOPIC_TOTAL_IMPORT'), 107 | ); 108 | $template->param( txtMQTTTopicImport => $txtMQTTTopicImport ); 109 | 110 | # --------------------------------------------------- 111 | # Control for "txtMQTTTopicExport" Textfield 112 | # --------------------------------------------------- 113 | my $txtMQTTTopicExport = $cgi->textfield( 114 | -name => 'txtMQTTTopicExport', 115 | -default => $pcfg->param('CONFIGURATION.TOPIC_TOTAL_EXPORT'), 116 | ); 117 | $template->param( txtMQTTTopicExport => $txtMQTTTopicExport ); 118 | 119 | # --------------------------------------------------- 120 | # Control for "txtCorrectionFactor" Textfield 121 | # --------------------------------------------------- 122 | my $txtCorrectionFactor = $cgi->textfield( 123 | -name => 'txtCorrectionFactor', 124 | -default => $pcfg->param('CONFIGURATION.CORRFACTOR'), 125 | ); 126 | $template->param( txtCorrectionFactor => $txtCorrectionFactor ); 127 | 128 | # --------------------------------------------------- 129 | # Control for "txtSerialPort" Textfield 130 | # --------------------------------------------------- 131 | my $txtSerialPort = $cgi->textfield( 132 | -name => 'txtSerialPort', 133 | -default => $pcfg->param('CONFIGURATION.SERIAL_PORT'), 134 | ); 135 | $template->param( txtSerialPort => $txtSerialPort ); 136 | 137 | # --------------------------------------------------- 138 | # Control for "btnSave" Button 139 | # --------------------------------------------------- 140 | my $btnSave = $cgi->submit( 141 | -name => 'btnSave', 142 | -value => $L{'CONFIGURATION.btnSave'}, 143 | ); 144 | $template->param( btnSave => $btnSave ); 145 | 146 | # --------------------------------------------------- 147 | # Control for "btnRestart" Button 148 | # --------------------------------------------------- 149 | my $btnRestart = $cgi->submit( 150 | -name => 'btnRestart', 151 | -value => $L{'CONFIGURATION.btnRestart'}, 152 | ); 153 | $template->param( btnRestart => $btnRestart); 154 | 155 | # --------------------------------------------------- 156 | # Control for "btnStart" Button 157 | # --------------------------------------------------- 158 | my $btnStart = $cgi->submit( 159 | -name => 'btnStart', 160 | -value => $L{'CONFIGURATION.btnStart'}, 161 | ); 162 | $template->param( btnStart => $btnStart); 163 | 164 | # --------------------------------------------------- 165 | # Control for "btnStop" Button 166 | # --------------------------------------------------- 167 | my $btnStop = $cgi->submit( 168 | -name => 'btnStop', 169 | -value => $L{'CONFIGURATION.btnStop'}, 170 | ); 171 | $template->param( btnStop => $btnStop); 172 | 173 | # Wir erzeugen eine Select-Liste mit 2 Einträgen für ON und OFF 174 | @values = ('1', '0' ); 175 | %labels = ( 176 | '1' => 'On', 177 | '0' => 'Off', 178 | ); 179 | my $selectlist = $cgi->popup_menu( 180 | -name => 'selectlist', 181 | -values => \@values, 182 | -labels => \%labels, 183 | -default => '1', 184 | ); 185 | $template->param( SELECTLIST => $selectlist ); 186 | 187 | 188 | # Den so erzeugten HTML-Code schreiben wir ins Template. 189 | 190 | $template->param( ACTIVATED => $activated); 191 | 192 | # --------------------------------------------------- 193 | # Localized Labels from language.ini 194 | # --------------------------------------------------- 195 | 196 | $template->param( lblMQTTTopicConsumption => $L{'CONFIGURATION.lblMQTTTopicConsumption'} ); 197 | $template->param( lblMQTTTopicImport => $L{'CONFIGURATION.lblMQTTTopicImport'} ); 198 | $template->param( lblMQTTTopicExport => $L{'CONFIGURATION.lblMQTTTopicExport'} ); 199 | $template->param( lblCorrectionFactor => $L{'CONFIGURATION.lblCorrectionFactor'} ); 200 | $template->param( lblSerialPort => $L{'CONFIGURATION.lblSerialPort'} ); 201 | 202 | # Nun wird das Template ausgegeben. 203 | print $template->output(); 204 | 205 | # Schlussendlich lassen wir noch den Footer ausgeben. 206 | LoxBerry::Web::lbfooter(); 207 | --------------------------------------------------------------------------------