diff --git a/packages/modules/chargepoints/internal_openwb/socket.py b/packages/modules/chargepoints/internal_openwb/socket.py index d02b23dd9d..0762db6481 100644 --- a/packages/modules/chargepoints/internal_openwb/socket.py +++ b/packages/modules/chargepoints/internal_openwb/socket.py @@ -48,8 +48,9 @@ class ActorState(IntEnum): class Socket(ChargepointModule): - def __init__(self, max_current: int, config: InternalOpenWB) -> None: - self.max_current = max_current + def __init__(self, socket_max_current: int, config: InternalOpenWB) -> None: + log.debug("Konfiguration als Buchse.") + self.socket_max_current = socket_max_current super().__init__(config) def set_current(self, current: float) -> None: @@ -60,12 +61,12 @@ def set_current(self, current: float) -> None: log.error("Error getting actor status! Using default 'opened'.") actor = ActorState.OPENED - if actor == ActorState.CLOSED: - if current == self.set_current_evse or self.chargepoint_state.plug_state is False: + if actor == ActorState.CLOSED or self.chargepoint_state.plug_state is False: + if current == self.set_current_evse: return else: current = 0 - super().set_current(min(current, self.max_current)) + super().set_current(min(current, self.socket_max_current)) def get_values(self, phase_switch_cp_active: bool) -> Tuple[ChargepointState, float]: try: @@ -94,6 +95,6 @@ def __close_actor(self): def __set_actor(self, open: bool): GPIO.output(23, GPIO.LOW if open else GPIO.HIGH) GPIO.output(26, GPIO.HIGH) - time.sleep(2 if open else 3) + time.sleep(1) GPIO.output(26, GPIO.LOW) log.debug("Actor opened" if open else "Actor closed") diff --git a/packages/modules/common/fault_state.py b/packages/modules/common/fault_state.py index e87fd7d93d..c45c25243e 100644 --- a/packages/modules/common/fault_state.py +++ b/packages/modules/common/fault_state.py @@ -45,7 +45,7 @@ def store_error(self, component_info: ComponentInfo) -> None: if ramdisk: topic = component_type.type_topic_mapping_comp(component_info.type) prefix = "openWB/set/" + topic + "/" - if component_info.id is not None: + if component_info.type != "counter" and component_info.type != "bat": if component_type == "vehicle": prefix += str(component_info.id) + "/socFault" else: diff --git a/packages/modules/devices/batterx/device.py b/packages/modules/devices/batterx/device.py index 178daa7d05..99566e2bf0 100644 --- a/packages/modules/devices/batterx/device.py +++ b/packages/modules/devices/batterx/device.py @@ -87,9 +87,9 @@ def read_legacy( dev = Device(device_config) dev = _add_component(dev, component_type, num) if evu_counter == "bezug_batterx": - dev = _add_component(dev, "counter", None) + dev = _add_component(dev, "counter", 0) if bat == "speicher_batterx": - dev = _add_component(dev, "bat", None) + dev = _add_component(dev, "bat", 3) log.debug('BatterX IP-Adresse: ' + ip_address) diff --git a/packages/modules/devices/e3dc/config.py b/packages/modules/devices/e3dc/config.py index f249addb2b..d238903697 100644 --- a/packages/modules/devices/e3dc/config.py +++ b/packages/modules/devices/e3dc/config.py @@ -12,7 +12,7 @@ def __init__(self, address: str = None): class E3dc: def __init__(self, name: str = "E3DC", - type: str = "E3DC", + type: str = "e3dc", id: int = 0, configuration: E3dcConfiguration = None) -> None: self.name = name diff --git a/runs/buchse.py b/runs/buchse.py deleted file mode 100755 index 1552b4989b..0000000000 --- a/runs/buchse.py +++ /dev/null @@ -1,440 +0,0 @@ -#!/usr/bin/python -import os -import time -import struct -import traceback -from typing import Tuple -import RPi.GPIO as GPIO -from pymodbus.client.sync import ModbusSerialClient -import paho.mqtt.client as mqtt - -basePath = "/var/www/html/openWB" -ramdiskPath = basePath + "/ramdisk" -logFilename = ramdiskPath + "/buchse.log" - -DeviceValues = {} -Values = {} - - -# handling of all logging statements -def log_debug(level: int, msg: str, traceback_str: str = None) -> None: - if level >= loglevel: - with open(logFilename, 'a') as log_file: - log_file.write(time.ctime() + ': ' + msg + '\n') - if traceback_str is not None: - log_file.write(traceback_str + '\n') - - -# write value to file in ramdisk -def write_to_ramdisk(filename: str, content: str) -> None: - with open(ramdiskPath + "/" + filename, "w") as file: - file.write(content) - - -# read value from file in ramdisk -def read_from_ramdisk(filename: str) -> str: - try: - with open(ramdiskPath + "/" + filename, 'r') as file: - return file.read() - except FileNotFoundError: - log_debug(2, "Error reading file '" + filename + "' from ramdisk!", traceback.format_exc()) - return "" - - -def init_gpio() -> None: - GPIO.setwarnings(False) - GPIO.setmode(GPIO.BOARD) - GPIO.setup(37, GPIO.OUT) - GPIO.setup(13, GPIO.OUT) - GPIO.setup(22, GPIO.OUT) - GPIO.setup(29, GPIO.OUT) - GPIO.setup(11, GPIO.OUT) - # GPIOs for socket - GPIO.setup(23, GPIO.OUT) - GPIO.setup(26, GPIO.OUT) - GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP) - - -def init_values() -> None: - global DeviceValues - global Values - # values LP1 - DeviceValues.update({'lp1voltage1': str(5)}) - DeviceValues.update({'lp1voltage2': str(5)}) - DeviceValues.update({'lp1voltage3': str(5)}) - DeviceValues.update({'lp1lla1': str(5)}) - DeviceValues.update({'lp1lla2': str(5)}) - DeviceValues.update({'lp1lla3': str(5)}) - DeviceValues.update({'lp1llkwh': str(5)}) - DeviceValues.update({'lp1watt': str(5)}) - DeviceValues.update({'lp1chargestat': str(5)}) - DeviceValues.update({'lp1plugstat': str(5)}) - DeviceValues.update({'lp1readerror': str(0)}) - Values.update({'lp1plugstat': str(5)}) - Values.update({'lp1chargestat': str(5)}) - Values.update({'lp1evsell': str(1)}) - - -# read all meter values and publish to mqtt broker -def read_meter(): - global evsefailure - global client - global llmeterconfiglp1 - global sdmid - - if (llmeterconfiglp1 == 0): - log_debug(2, "Erkenne verbauten Zaehler.") - # check sdm - try: - resp = client.read_input_registers(0x00, 2, unit=105) - voltage = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - if int(voltage) > 20: - llmeterconfiglp1 = 105 - sdmid = 105 - log_debug(2, "SDM Zaehler erkannt") - except AttributeError: - log_debug(2, "SDM check failed", traceback.format_exc()) - # check B23 - try: - resp = client.read_holding_registers(0x5B00, 2, unit=201) - voltage = resp.registers[1] - if int(voltage) > 20: - llmeterconfiglp1 = 201 - sdmid = 201 - log_debug(2, "B23 Zaehler erkannt") - except AttributeError: - log_debug(2, "B23 check failed", traceback.format_exc()) - else: - sdmid = llmeterconfiglp1 - try: - if sdmid < 200: - # SDM - resp = client.read_input_registers(0x0C, 2, unit=sdmid) - lp1llw1 = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1llw1 = int(lp1llw1) - resp = client.read_input_registers(0x0E, 2, unit=sdmid) - lp1llw2 = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1llw2 = int(lp1llw2) - resp = client.read_input_registers(0x10, 2, unit=sdmid) - lp1llw3 = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1llw3 = int(lp1llw3) - lp1llg = lp1llw1 + lp1llw2 + lp1llw3 - if lp1llg < 10: - lp1llg = 0 - write_to_ramdisk("llaktuell", str(lp1llg)) - resp = client.read_input_registers(0x00, 2, unit=sdmid) - voltage = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1voltage1 = float("%.1f" % voltage) - write_to_ramdisk("llv1", str(lp1voltage1)) - resp = client.read_input_registers(0x02, 2, unit=sdmid) - voltage = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1voltage2 = float("%.1f" % voltage) - write_to_ramdisk("llv2", str(lp1voltage2)) - resp = client.read_input_registers(0x04, 2, unit=sdmid) - voltage = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1voltage3 = float("%.1f" % voltage) - write_to_ramdisk("llv3", str(lp1voltage3)) - resp = client.read_input_registers(0x06, 2, unit=sdmid) - lp1lla1 = float(struct.unpack('>f', struct.pack('>HH', *resp.registers))[0]) - lp1lla1 = float("%.1f" % lp1lla1) - write_to_ramdisk("lla1", str(lp1lla1)) - resp = client.read_input_registers(0x08, 2, unit=sdmid) - lp1lla2 = float(struct.unpack('>f', struct.pack('>HH', *resp.registers))[0]) - lp1lla2 = float("%.1f" % lp1lla2) - write_to_ramdisk("lla2", str(lp1lla2)) - resp = client.read_input_registers(0x0A, 2, unit=sdmid) - lp1lla3 = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1lla3 = float("%.1f" % lp1lla3) - write_to_ramdisk("lla3", str(lp1lla3)) - resp = client.read_input_registers(0x0156, 2, unit=sdmid) - lp1llkwh = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - lp1llkwh = float("%.3f" % lp1llkwh) - write_to_ramdisk("llkwh", str(lp1llkwh)) - resp = client.read_input_registers(0x46, 2, unit=sdmid) - hz = struct.unpack('>f', struct.pack('>HH', *resp.registers))[0] - hz = float("%.2f" % hz) - write_to_ramdisk("llhz", str(hz)) - else: - # B23 - # llkwh - resp = client.read_holding_registers(0x5000, 4, unit=sdmid) - lp1llkwh = struct.unpack('>Q', struct.pack('>HHHH', *resp.registers))[0]/100 - write_to_ramdisk("llkwh", str(lp1llkwh)) - # Voltage - resp = client.read_holding_registers(0x5B00, 2, unit=sdmid) - voltage = resp.registers[1] - lp1voltage1 = float(voltage) / 10 - write_to_ramdisk("llv1", str(lp1voltage1)) - resp = client.read_holding_registers(0x5B02, 2, unit=sdmid) - lp1voltage2 = resp.registers[1] - lp1voltage2 = float(lp1voltage2) / 10 - write_to_ramdisk("llv2", str(lp1voltage2)) - resp = client.read_holding_registers(0x5B04, 2, unit=sdmid) - voltage = resp.registers[1] - lp1voltage3 = float(voltage) / 10 - write_to_ramdisk("llv3", str(lp1voltage3)) - # Ampere - resp = client.read_holding_registers(0x5B0C, 2, unit=sdmid) - amp = resp.registers[1] - lp1lla1 = float(amp) / 100 - write_to_ramdisk("lla1", str(lp1lla1)) - resp = client.read_holding_registers(0x5B0E, 2, unit=sdmid) - amp = resp.registers[1] - lp1lla2 = float(amp) / 100 - write_to_ramdisk("lla2", str(lp1lla2)) - resp = client.read_holding_registers(0x5B10, 2, unit=sdmid) - amp = resp.registers[1] - lp1lla3 = float(amp) / 100 - write_to_ramdisk("lla3", str(lp1lla3)) - # Gesamt watt - resp = client.read_holding_registers(0x5B14, 2, unit=sdmid) - lp1llg = int(struct.unpack('>i', struct.pack('>HH', *resp.registers))[0]/100) - write_to_ramdisk("llaktuell", str(lp1llg)) - # LL Hz - resp = client.read_holding_registers(0x5B2C, 2, unit=sdmid) - hz = float(resp.registers[0]) / 100 - write_to_ramdisk("llhz", str(hz)) - - try: - time.sleep(0.1) - rq = client.read_holding_registers(1000, 1, unit=1) - lp1ll = rq.registers[0] - evsefailure = 0 - except: - lp1ll = 0 - evsefailure = 1 - try: - time.sleep(0.1) - rq = client.read_holding_registers(1002, 1, unit=1) - lp1var = rq.registers[0] - evsefailure = 0 - DeviceValues.update({'lp1readerror': str(0)}) - except: - DeviceValues.update({'lp1readerror': str(int(DeviceValues['lp1readerror'])+1)}) - log_debug(2, "Fehler!", traceback.format_exc()) - lp1var = 5 - evsefailure = 1 - if (lp1var == 5 and int(DeviceValues['lp1readerror']) > MaxEvseError): - log_debug(2, "Anhaltender Fehler beim Auslesen der EVSE von lp1! (" + - str(DeviceValues['lp1readerror']) + ")") - log_debug(2, "Plugstat und Chargestat werden zurückgesetzt.") - Values.update({'lp1plugstat': 0}) - Values.update({'lp1chargestat': 0}) - elif (lp1var == 1): - Values.update({'lp1plugstat': 0}) - Values.update({'lp1chargestat': 0}) - elif (lp1var == 2): - Values.update({'lp1plugstat': 1}) - Values.update({'lp1chargestat': 0}) - elif (lp1var == 3 and lp1ll > 0): - Values.update({'lp1plugstat': 1}) - Values.update({'lp1chargestat': 1}) - elif (lp1var == 3 and lp1ll == 0): - Values.update({'lp1plugstat': 1}) - Values.update({'lp1chargestat': 0}) - write_to_ramdisk("plugstat", str(Values["lp1plugstat"])) - write_to_ramdisk("chargestat", str(Values["lp1chargestat"])) - Values.update({'lp1evsell': lp1ll}) - log_debug(0, "EVSE lp1plugstat: " + str(lp1var) + " EVSE lp1LL: " + str(lp1ll)) - - mclient = mqtt.Client("openWB-buchse-bulkpublisher-" + str(os.getpid())) - mclient.connect("localhost") - mclient.loop(timeout=2.0) - for key in DeviceValues: - if ("lp1watt" in key): - if (DeviceValues[str(key)] != str(lp1llg)): - mclient.publish("openWB/lp/1/W", payload=str(lp1llg), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1watt': str(lp1llg)}) - if ("lp1voltage1" in key): - if (DeviceValues[str(key)] != str(lp1voltage1)): - mclient.publish("openWB/lp/1/VPhase1", payload=str(lp1voltage1), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1voltage1': str(lp1voltage1)}) - if ("lp1voltage2" in key): - if (DeviceValues[str(key)] != str(lp1voltage2)): - mclient.publish("openWB/lp/1/VPhase2", payload=str(lp1voltage2), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1voltage2': str(lp1voltage2)}) - if ("lp1voltage3" in key): - if (DeviceValues[str(key)] != str(lp1voltage3)): - mclient.publish("openWB/lp/1/VPhase3", payload=str(lp1voltage3), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1voltage3': str(lp1voltage3)}) - if ("lp1lla1" in key): - if (DeviceValues[str(key)] != str(lp1lla1)): - mclient.publish("openWB/lp/1/APhase1", payload=str(lp1lla1), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1lla1': str(lp1lla1)}) - if ("lp1lla2" in key): - if (DeviceValues[str(key)] != str(lp1lla2)): - mclient.publish("openWB/lp/1/APhase2", payload=str(lp1lla2), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1lla2': str(lp1lla2)}) - if ("lp1lla3" in key): - if (DeviceValues[str(key)] != str(lp1lla3)): - mclient.publish("openWB/lp/1/APhase3", payload=str(lp1lla3), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1lla3': str(lp1lla3)}) - if ("lp1llkwh" in key): - if (DeviceValues[str(key)] != str(lp1llkwh)): - mclient.publish("openWB/lp/1/kWhCounter", payload=str(lp1llkwh), qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1llkwh': str(lp1llkwh)}) - if ("lp1plugstat" in key): - if (DeviceValues[str(key)] != Values["lp1plugstat"]): - mclient.publish("openWB/lp/1/boolPlugStat", payload=Values["lp1plugstat"], qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1plugstat': Values["lp1plugstat"]}) - if ("lp1chargestat" in key): - if (DeviceValues[str(key)] != Values["lp1chargestat"]): - mclient.publish("openWB/lp/1/boolChargeStat", payload=Values["lp1chargestat"], qos=0, retain=True) - mclient.loop(timeout=2.0) - DeviceValues.update({'lp1chargestat': Values["lp1chargestat"]}) - mclient.disconnect() - except Exception: - log_debug(2, "Get meter Fehler!", traceback.format_exc()) - - -# control of socket lock -# GPIO 23: control direction of lock motor -# GPIO 26: power to lock motor -def set_socket_actuator(action): - if action == "auf": - GPIO.output(23, GPIO.LOW) - GPIO.output(26, GPIO.HIGH) - time.sleep(2) - GPIO.output(26, GPIO.LOW) - log_debug(1, "Aktor auf") - if action == "zu": - GPIO.output(23, GPIO.HIGH) - GPIO.output(26, GPIO.HIGH) - time.sleep(3) - GPIO.output(26, GPIO.LOW) - log_debug(1, "Aktor zu") - - -# get actual socket lock state -def get_socket_state() -> int: - actorstat_tmp = GPIO.input(19) - if actorstat_tmp == GPIO.LOW: - return 1 - else: - return 0 - - -# get all values to control our chargepoint -def load_control_values(): - global actorstat - global lp1solla - global u1p3pstat - global u1p3ptmpstat - global evsefailure - - actorstat = get_socket_state() - try: - lp1solla = int(read_from_ramdisk("llsoll")) - except ValueError: - lp1solla = 0 - log_debug(0, "LL Soll: " + str(lp1solla) + " ActorStatus: " + str(actorstat)) - if socket_configured: - if (evsefailure == 0): - if (Values["lp1plugstat"] == 1): - if (actorstat == 0): - set_socket_actuator("zu") - if (Values["lp1plugstat"] == 0): - if (actorstat == 1): - writelp1evse(0) - set_socket_actuator("auf") - if (actorstat == 1): - if (Values["lp1evsell"] != lp1solla and Values["lp1plugstat"] == 1): - writelp1evse(lp1solla) - else: - if (Values["lp1evsell"] != 0): - writelp1evse(0) - else: - if (Values["lp1evsell"] != lp1solla): - writelp1evse(lp1solla) - try: - u1p3ptmpstat = int(read_from_ramdisk("u1p3pstat")) - except ValueError: - u1p3ptmpstat = 3 - try: - u1p3pstat - except: - u1p3pstat = 3 - u1p3pstat = switch_phases_cp1(u1p3ptmpstat, u1p3pstat) - - -def __switch_phases(gpio_cp: int, gpio_relay: int): - GPIO.output(gpio_cp, GPIO.HIGH) # CP on - GPIO.output(gpio_relay, GPIO.HIGH) # 3 on/off - time.sleep(2) - GPIO.output(gpio_relay, GPIO.LOW) # 3 on/off - time.sleep(5) - GPIO.output(gpio_cp, GPIO.LOW) # CP off - time.sleep(1) - - -def switch_phases_cp1(new_phases: int, old_phases: int) -> int: - if (new_phases != old_phases): - log_debug(1, "switching phases on cp1: old=" + str(old_phases) + " new=" + str(new_phases)) - gpio_cp = 22 - if (new_phases == 1): - gpio_relay = 29 - else: - gpio_relay = 37 - __switch_phases(gpio_cp, gpio_relay) - else: - log_debug(0, "no need to switch phases on cp1: old=" + str(old_phases) + " new=" + str(new_phases)) - return new_phases - - -def writelp1evse(lla): - if (lla > pp): - lla = pp - client.write_registers(1000, lla, unit=1) - log_debug(1, "Write to EVSE lp1 " + str(lla)) - - -# check for "openWB Buchse" -def check_for_socket() -> Tuple[bool, int]: - try: - with open('/home/pi/ppbuchse', 'r') as value: - pp_value = int(value.read()) - except (FileNotFoundError, ValueError): - pp_value = 32 - # here we always have a socket - socket_is_configured = True - log_debug(1, "check for socket: " + str(socket_is_configured) + " " + str(pp_value)) - return [socket_is_configured, pp_value] - - -# guess USB/modbus device name -def detect_modbus_usb_port() -> str: - try: - with open("/dev/ttyUSB0"): - return "/dev/ttyUSB0" - except FileNotFoundError: - return "/dev/serial0" - - -loglevel = 1 - -MaxEvseError = 5 -sdmid = 105 -actorstat = 0 -evsefailure = 0 -llmeterconfiglp1 = 0 - -init_gpio() -init_values() -socket_configured, pp = check_for_socket() -seradd = detect_modbus_usb_port() -# connect with USB/modbus device -with ModbusSerialClient(method="rtu", port=seradd, baudrate=9600, stopbits=1, bytesize=8, timeout=1) as client: - # start our control loop - while True: - read_meter() - load_control_values() - time.sleep(1) diff --git a/runs/initRamdisk.sh b/runs/initRamdisk.sh index c3fc53f58b..ffe5f85d89 100755 --- a/runs/initRamdisk.sh +++ b/runs/initRamdisk.sh @@ -373,6 +373,7 @@ initRamdisk(){ echo 0 > $RamdiskPath/socketApproved echo 0 > $RamdiskPath/socketActivated echo 0 > $RamdiskPath/socketActivationRequested + echo "unknown" > $RamdiskPath/prev_isss_mode # diverse Dateien echo 0 > $RamdiskPath/AllowedTotalCurrentPerPhase diff --git a/runs/isss.py b/runs/isss.py index 84c7aa7401..e0fa579027 100755 --- a/runs/isss.py +++ b/runs/isss.py @@ -1,20 +1,22 @@ #!/usr/bin/python +from enum import Enum import logging import os import re +import sys import threading import time -from typing import Dict, List, Optional +from typing import List, Optional import RPi.GPIO as GPIO from helpermodules.pub import pub_single -from helpermodules import compatibility from modules.common.store import ramdisk_read, ramdisk_write from modules.common.store._util import get_rounding_function_by_digits from modules.common.fault_state import FaultState from modules.common.component_state import ChargepointState from modules.common.modbus import ModbusSerialClient_ -from modules.chargepoints.internal_openwb import chargepoint_module, socket +from modules.chargepoints.internal_openwb import chargepoint_module +from modules.chargepoints.internal_openwb.socket import Socket from modules.chargepoints.internal_openwb.chargepoint_module import InternalOpenWB basePath = "/var/www/html/openWB" @@ -23,6 +25,12 @@ MAP_LOG_LEVEL = [logging.ERROR, logging.WARNING, logging.DEBUG] +class IsssMode(Enum): + SOCKET = "socket" + DUO = "duo" + DAEMON = "daemon" + + logging.basicConfig(filename=ramdiskPath+'/isss.log', format='%(asctime)s - {%(name)s:%(lineno)s} - %(levelname)s - %(message)s', level=MAP_LOG_LEVEL[int(os.environ.get('debug'))]) @@ -193,17 +201,14 @@ def __thread_cp_interruption(self, duration: int) -> None: class Isss: - def __init__(self) -> None: + def __init__(self, mode: IsssMode, socket_max_current: int) -> None: log.debug("Init isss") self.serial_client = ModbusSerialClient_(self.detect_modbus_usb_port()) - self.cp0 = IsssChargepoint(self.serial_client, 0) - try: - if int(ramdisk_read("issslp2act")) == 1: - self.cp1 = IsssChargepoint(self.serial_client, 1) - else: - self.cp1 = None - except (FileNotFoundError, ValueError) as e: - log.error("Error reading issslp2act! Guessing cp2 is not configured.") + self.cp0 = IsssChargepoint(self.serial_client, 0, mode, socket_max_current) + if mode == IsssMode.DUO: + log.debug("Zweiter Ladepunkt für Duo konfiguriert.") + self.cp1 = IsssChargepoint(self.serial_client, 1, mode, socket_max_current) + else: self.cp1 = None self.init_gpio() @@ -263,14 +268,12 @@ def get_parent_wb() -> str: class IsssChargepoint: - def __init__(self, serial_client: ModbusSerialClient_, local_charge_point_num: int) -> None: + def __init__(self, serial_client: ModbusSerialClient_, local_charge_point_num: int, mode: IsssMode, socket_max_current: int) -> None: self.local_charge_point_num = local_charge_point_num if local_charge_point_num == 0: - try: - with open('/home/pi/ppbuchse', 'r') as f: - max_current = int(f.read()) - self.module = socket.Socket(max_current, InternalOpenWB(0, serial_client)) - except (FileNotFoundError, ValueError): + if mode == IsssMode.SOCKET: + self.module = Socket(socket_max_current, InternalOpenWB(0, serial_client)) + else: self.module = chargepoint_module.ChargepointModule(InternalOpenWB(0, serial_client)) else: self.module = chargepoint_module.ChargepointModule(InternalOpenWB(1, serial_client)) @@ -297,4 +300,4 @@ def __thread_active(thread: Optional[threading.Thread]) -> bool: log.exception("Fehler bei Ladepunkt "+str(self.local_charge_point_num)) -Isss().loop() +Isss(IsssMode(sys.argv[1]), int(sys.argv[2])).loop() diff --git a/runs/services.sh b/runs/services.sh index c64db615f3..c8492e6ac0 100755 --- a/runs/services.sh +++ b/runs/services.sh @@ -63,18 +63,37 @@ start() { fi - if (( isss == 1 )) || [[ "$evsecon" == "daemon" ]]; then - openwbDebugLog "MAIN" 1 "external openWB or daemon mode configured" - if pgrep -f '^python.*/isss.py' > /dev/null + if (( isss == 1 )) || [[ "$evsecon" == "daemon" ]] || [[ "$evsecon" == "buchse" ]]; then + if [[ "$evsecon" == "buchse" ]]; then + isss_mode="socket" + elif [[ $lastmanagement == 1 ]]; then + isss_mode="duo" + else + isss_mode="daemon" + fi + prev_isss_mode=$(< $OPENWBBASEDIR/ramdisk/isss_mode) + + openwbDebugLog "MAIN" 1 "external openWB, daemon mode or socket mode configured" + if pgrep -f '^python.*/isss.py' > /dev/null && [[ $prev_isss_mode == $isss_mode ]]; then openwbDebugLog "MAIN" 1 "isss handler already running" else - openwbDebugLog "MAIN" 0 "isss handler not running! restarting process" - echo "$lastmanagement" > "$OPENWBBASEDIR/ramdisk/issslp2act" - nohup python3 "$OPENWBBASEDIR/runs/isss.py" >>"$OPENWBBASEDIR/ramdisk/isss.log" 2>&1 & + openwbDebugLog "MAIN" 0 "Start/restart isss handler in mode $isss_mode." + if [ -f /home/pi/ppbuchse ]; then + ppbuchse=$(< /home/pi/ppbuchse) + re='^[0-9]+$' + if ! [[ $ppbuchse =~ $re ]] ; then + openwbDebugLog "MAIN" 0 "Invalid value in ppbuchse. Set ppbuchse to 32." + ppbuchse=32 + fi + else + ppbuchse=32 + fi + nohup python3 "$OPENWBBASEDIR/runs/isss.py" "$isss_mode" "$ppbuchse">>"$OPENWBBASEDIR/ramdisk/isss.log" 2>&1 & fi + echo "$isss_mode" > $OPENWBBASEDIR/ramdisk/isss_mode else - openwbDebugLog "MAIN" 1 "external openWB or daemon mode not configured; checking network setup" + openwbDebugLog "MAIN" 1 "external openWB, daemon mode or socket mode not configured; checking network setup" local ethstate=$( /home/pi/ppbuchse - - if pgrep -f '^python.*/buchse.py' > /dev/null - then - openwbDebugLog "MAIN" 1 "socket handler already running" - else - openwbDebugLog "MAIN" 0 "socket handler not running! restarting process" - nohup python3 "$OPENWBBASEDIR/runs/buchse.py" >> "$OPENWBBASEDIR/ramdisk/openWB.log" 2>&1 & - fi - else - sudo pkill -f '^python.*/buchse.py' - fi - rseSetup "$rseenabled" 0 pushButtonsSetup "$ladetaster" 0 diff --git a/web/display/minimal/gauge.js b/web/display/minimal/gauge.js new file mode 100644 index 0000000000..03780321c5 --- /dev/null +++ b/web/display/minimal/gauge.js @@ -0,0 +1,1030 @@ +// Generated by CoffeeScript 1.11.1 +(function () { + var AnimatedText, AnimatedTextFactory, Bar, BaseDonut, BaseGauge, Donut, Gauge, GaugePointer, TextRenderer, ValueUpdater, addCommas, cutHex, formatNumber, mergeObjects, secondsToString, + slice = [].slice, + hasProp = {}.hasOwnProperty, + extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + indexOf = [].indexOf || function (item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + (function () { + var browserRequestAnimationFrame, isCancelled, j, lastId, len, vendor, vendors; + vendors = ['ms', 'moz', 'webkit', 'o']; + for (j = 0, len = vendors.length; j < len; j++) { + vendor = vendors[j]; + if (window.requestAnimationFrame) { + break; + } + window.requestAnimationFrame = window[vendor + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendor + 'CancelAnimationFrame'] || window[vendor + 'CancelRequestAnimationFrame']; + } + browserRequestAnimationFrame = null; + lastId = 0; + isCancelled = {}; + if (!requestAnimationFrame) { + window.requestAnimationFrame = function (callback, element) { + var currTime, id, lastTime, timeToCall; + currTime = new Date().getTime(); + timeToCall = Math.max(0, 16 - (currTime - lastTime)); + id = window.setTimeout(function () { + return callback(currTime + timeToCall); + }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + return window.cancelAnimationFrame = function (id) { + return clearTimeout(id); + }; + } else if (!window.cancelAnimationFrame) { + browserRequestAnimationFrame = window.requestAnimationFrame; + window.requestAnimationFrame = function (callback, element) { + var myId; + myId = ++lastId; + browserRequestAnimationFrame(function () { + if (!isCancelled[myId]) { + return callback(); + } + }, element); + return myId; + }; + return window.cancelAnimationFrame = function (id) { + return isCancelled[id] = true; + }; + } + })(); + + secondsToString = function (sec) { + var hr, min; + hr = Math.floor(sec / 3600); + min = Math.floor((sec - (hr * 3600)) / 60); + sec -= (hr * 3600) + (min * 60); + sec += ''; + min += ''; + while (min.length < 2) { + min = '0' + min; + } + while (sec.length < 2) { + sec = '0' + sec; + } + hr = hr ? hr + ':' : ''; + return hr + min + ':' + sec; + }; + + formatNumber = function () { + var digits, num, value; + num = 1 <= arguments.length ? slice.call(arguments, 0) : []; + value = num[0]; + digits = 0 || num[1]; + return addCommas(value.toFixed(digits)); + }; + + mergeObjects = function (obj1, obj2) { + var key, out, val; + out = {}; + for (key in obj1) { + if (!hasProp.call(obj1, key)) continue; + val = obj1[key]; + out[key] = val; + } + for (key in obj2) { + if (!hasProp.call(obj2, key)) continue; + val = obj2[key]; + out[key] = val; + } + return out; + }; + + addCommas = function (nStr) { + var rgx, x, x1, x2; + nStr += ''; + x = nStr.split('.'); + x1 = x[0]; + x2 = ''; + if (x.length > 1) { + x2 = '.' + x[1]; + } + rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; + }; + + cutHex = function (nStr) { + if (nStr.charAt(0) === "#") { + return nStr.substring(1, 7); + } + return nStr; + }; + + ValueUpdater = (function () { + ValueUpdater.prototype.animationSpeed = 32; + + function ValueUpdater(addToAnimationQueue, clear) { + if (addToAnimationQueue == null) { + addToAnimationQueue = true; + } + this.clear = clear != null ? clear : true; + if (addToAnimationQueue) { + AnimationUpdater.add(this); + } + } + + ValueUpdater.prototype.update = function (force) { + var diff; + if (force == null) { + force = false; + } + if (force || this.displayedValue !== this.value) { + if (this.ctx && this.clear) { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + diff = this.value - this.displayedValue; + if (Math.abs(diff / this.animationSpeed) <= 0.001) { + this.displayedValue = this.value; + } else { + this.displayedValue = this.displayedValue + diff / this.animationSpeed; + } + this.render(); + return true; + } + return false; + }; + + return ValueUpdater; + + })(); + + BaseGauge = (function (superClass) { + extend(BaseGauge, superClass); + + function BaseGauge() { + return BaseGauge.__super__.constructor.apply(this, arguments); + } + + BaseGauge.prototype.displayScale = 1; + + BaseGauge.prototype.forceUpdate = true; + + BaseGauge.prototype.setTextField = function (textField, fractionDigits) { + return this.textField = textField instanceof TextRenderer ? textField : new TextRenderer(textField, fractionDigits); + }; + + BaseGauge.prototype.setMinValue = function (minValue, updateStartValue) { + var gauge, j, len, ref, results; + this.minValue = minValue; + if (updateStartValue == null) { + updateStartValue = true; + } + if (updateStartValue) { + this.displayedValue = this.minValue; + ref = this.gp || []; + results = []; + for (j = 0, len = ref.length; j < len; j++) { + gauge = ref[j]; + results.push(gauge.displayedValue = this.minValue); + } + return results; + } + }; + + BaseGauge.prototype.setOptions = function (options) { + if (options == null) { + options = null; + } + this.options = mergeObjects(this.options, options); + if (this.textField) { + this.textField.el.style.fontSize = options.fontSize + 'px'; + } + if (this.options.angle > .5) { + this.options.angle = .5; + } + this.configDisplayScale(); + return this; + }; + + BaseGauge.prototype.configDisplayScale = function () { + var backingStorePixelRatio, devicePixelRatio, height, prevDisplayScale, width; + prevDisplayScale = this.displayScale; + if (this.options.highDpiSupport === false) { + delete this.displayScale; + } else { + devicePixelRatio = window.devicePixelRatio || 1; + backingStorePixelRatio = this.ctx.webkitBackingStorePixelRatio || this.ctx.mozBackingStorePixelRatio || this.ctx.msBackingStorePixelRatio || this.ctx.oBackingStorePixelRatio || this.ctx.backingStorePixelRatio || 1; + this.displayScale = devicePixelRatio / backingStorePixelRatio; + } + if (this.displayScale !== prevDisplayScale) { + width = this.canvas.G__width || this.canvas.width; + height = this.canvas.G__height || this.canvas.height; + this.canvas.width = width * this.displayScale; + this.canvas.height = height * this.displayScale; + this.canvas.style.width = width + "px"; + this.canvas.style.height = height + "px"; + this.canvas.G__width = width; + this.canvas.G__height = height; + } + return this; + }; + + BaseGauge.prototype.parseValue = function (value) { + value = parseFloat(value) || Number(value); + if (isFinite(value)) { + return value; + } else { + return 0; + } + }; + + return BaseGauge; + + })(ValueUpdater); + + TextRenderer = (function () { + function TextRenderer(el, fractionDigits1) { + this.el = el; + this.fractionDigits = fractionDigits1; + } + + TextRenderer.prototype.render = function (gauge) { + return this.el.innerHTML = formatNumber(gauge.displayedValue, this.fractionDigits); + }; + + return TextRenderer; + + })(); + + AnimatedText = (function (superClass) { + extend(AnimatedText, superClass); + + AnimatedText.prototype.displayedValue = 0; + + AnimatedText.prototype.value = 0; + + AnimatedText.prototype.setVal = function (value) { + return this.value = 1 * value; + }; + + function AnimatedText(elem1, text) { + this.elem = elem1; + this.text = text != null ? text : false; + AnimatedText.__super__.constructor.call(this); + if (this.elem === void 0) { + throw new Error('The element isn\'t defined.'); + } + this.value = 1 * this.elem.innerHTML; + if (this.text) { + this.value = 0; + } + } + + AnimatedText.prototype.render = function () { + var textVal; + if (this.text) { + textVal = secondsToString(this.displayedValue.toFixed(0)); + } else { + textVal = addCommas(formatNumber(this.displayedValue)); + } + return this.elem.innerHTML = textVal; + }; + + return AnimatedText; + + })(ValueUpdater); + + AnimatedTextFactory = { + create: function (objList) { + var elem, j, len, out; + out = []; + for (j = 0, len = objList.length; j < len; j++) { + elem = objList[j]; + out.push(new AnimatedText(elem)); + } + return out; + } + }; + + GaugePointer = (function (superClass) { + extend(GaugePointer, superClass); + + GaugePointer.prototype.displayedValue = 0; + + GaugePointer.prototype.value = 0; + + GaugePointer.prototype.options = { + strokeWidth: 0.035, + length: 0.1, + color: "#000000", + iconPath: null, + iconScale: 1.0, + iconAngle: 0 + }; + + GaugePointer.prototype.img = null; + + function GaugePointer(gauge1) { + this.gauge = gauge1; + if (this.gauge === void 0) { + throw new Error('The element isn\'t defined.'); + } + this.ctx = this.gauge.ctx; + this.canvas = this.gauge.canvas; + GaugePointer.__super__.constructor.call(this, false, false); + this.setOptions(); + } + + GaugePointer.prototype.setOptions = function (options) { + if (options == null) { + options = null; + } + this.options = mergeObjects(this.options, options); + this.length = 2 * this.gauge.radius * this.gauge.options.radiusScale * this.options.length; + this.strokeWidth = this.canvas.height * this.options.strokeWidth; + this.maxValue = this.gauge.maxValue; + this.minValue = this.gauge.minValue; + this.animationSpeed = this.gauge.animationSpeed; + this.options.angle = this.gauge.options.angle; + if (this.options.iconPath) { + this.img = new Image(); + return this.img.src = this.options.iconPath; + } + }; + + GaugePointer.prototype.render = function () { + var angle, endX, endY, imgX, imgY, startX, startY, x, y; + angle = this.gauge.getAngle.call(this, this.displayedValue); + x = Math.round(this.length * Math.cos(angle)); + y = Math.round(this.length * Math.sin(angle)); + startX = Math.round(this.strokeWidth * Math.cos(angle - Math.PI / 2)); + startY = Math.round(this.strokeWidth * Math.sin(angle - Math.PI / 2)); + endX = Math.round(this.strokeWidth * Math.cos(angle + Math.PI / 2)); + endY = Math.round(this.strokeWidth * Math.sin(angle + Math.PI / 2)); + this.ctx.beginPath(); + this.ctx.fillStyle = this.options.color; + this.ctx.arc(0, 0, this.strokeWidth, 0, Math.PI * 2, false); + this.ctx.fill(); + this.ctx.beginPath(); + this.ctx.moveTo(startX, startY); + this.ctx.lineTo(x, y); + this.ctx.lineTo(endX, endY); + this.ctx.fill(); + if (this.img) { + imgX = Math.round(this.img.width * this.options.iconScale); + imgY = Math.round(this.img.height * this.options.iconScale); + this.ctx.save(); + this.ctx.translate(x, y); + this.ctx.rotate(angle + Math.PI / 180.0 * (90 + this.options.iconAngle)); + this.ctx.drawImage(this.img, -imgX / 2, -imgY / 2, imgX, imgY); + return this.ctx.restore(); + } + }; + + return GaugePointer; + + })(ValueUpdater); + + Bar = (function () { + function Bar(elem1) { + this.elem = elem1; + } + + Bar.prototype.updateValues = function (arrValues) { + this.value = arrValues[0]; + this.maxValue = arrValues[1]; + this.avgValue = arrValues[2]; + return this.render(); + }; + + Bar.prototype.render = function () { + var avgPercent, valPercent; + if (this.textField) { + this.textField.text(formatNumber(this.value)); + } + if (this.maxValue === 0) { + this.maxValue = this.avgValue * 2; + } + valPercent = (this.value / this.maxValue) * 100; + avgPercent = (this.avgValue / this.maxValue) * 100; + $(".bar-value", this.elem).css({ + "width": valPercent + "%" + }); + return $(".typical-value", this.elem).css({ + "width": avgPercent + "%" + }); + }; + + return Bar; + + })(); + + Gauge = (function (superClass) { + extend(Gauge, superClass); + + Gauge.prototype.elem = null; + + Gauge.prototype.value = [20]; + + Gauge.prototype.maxValue = 80; + + Gauge.prototype.minValue = 0; + + Gauge.prototype.displayedAngle = 0; + + Gauge.prototype.displayedValue = 0; + + Gauge.prototype.lineWidth = 40; + + Gauge.prototype.paddingTop = 0.1; + + Gauge.prototype.paddingBottom = 0.1; + + Gauge.prototype.percentColors = null; + + Gauge.prototype.options = { + colorStart: "#6fadcf", + colorStop: void 0, + gradientType: 0, + strokeColor: "#e0e0e0", + pointer: { + length: 0.8, + strokeWidth: 0.035, + iconScale: 1.0 + }, + angle: 0.15, + lineWidth: 0.44, + radiusScale: 1.0, + fontSize: 40, + limitMax: false, + limitMin: false, + range: 1, + mirror: false + }; + + function Gauge(canvas) { + var h, w; + this.canvas = canvas; + Gauge.__super__.constructor.call(this); + this.percentColors = null; + if (typeof G_vmlCanvasManager !== 'undefined') { + this.canvas = window.G_vmlCanvasManager.initElement(this.canvas); + } + this.ctx = this.canvas.getContext('2d'); + h = this.canvas.clientHeight; + w = this.canvas.clientWidth; + this.canvas.height = h; + this.canvas.width = w; + this.gp = [new GaugePointer(this)]; + this.setOptions(); + } + + Gauge.prototype.setOptions = function (options) { + var gauge, j, len, phi, ref; + if (options == null) { + options = null; + } + Gauge.__super__.setOptions.call(this, options); + this.configPercentColors(); + this.extraPadding = 0; + if (this.options.angle < 0) { + phi = Math.PI * (1 + this.options.angle); + this.extraPadding = Math.sin(phi); + } + this.availableHeight = this.canvas.height * (1 - this.paddingTop - this.paddingBottom); + this.lineWidth = this.availableHeight * this.options.lineWidth; + this.radius = (this.availableHeight - this.lineWidth / 2) / (1.0 + this.extraPadding); + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + ref = this.gp; + for (j = 0, len = ref.length; j < len; j++) { + gauge = ref[j]; + gauge.setOptions(this.options.pointer); + gauge.render(); + } + this.render(); + return this; + }; + + Gauge.prototype.configPercentColors = function () { + var bval, gval, i, j, ref, results, rval; + this.percentColors = null; + if (this.options.percentColors !== void 0) { + this.percentColors = new Array(); + results = []; + for (i = j = 0, ref = this.options.percentColors.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) { + rval = parseInt((cutHex(this.options.percentColors[i][1])).substring(0, 2), 16); + gval = parseInt((cutHex(this.options.percentColors[i][1])).substring(2, 4), 16); + bval = parseInt((cutHex(this.options.percentColors[i][1])).substring(4, 6), 16); + results.push(this.percentColors[i] = { + pct: this.options.percentColors[i][0], + color: { + r: rval, + g: gval, + b: bval + } + }); + } + return results; + } + }; + + Gauge.prototype.set = function (value) { + var gp, i, j, l, len, m, ref, ref1, val; + if (!(value instanceof Array)) { + value = [value]; + } + for (i = j = 0, ref = value.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) { + value[i] = this.parseValue(value[i]); + } + if (value.length > this.gp.length) { + for (i = l = 0, ref1 = value.length - this.gp.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) { + gp = new GaugePointer(this); + gp.setOptions(this.options.pointer); + this.gp.push(gp); + } + } else if (value.length < this.gp.length) { + this.gp = this.gp.slice(this.gp.length - value.length); + } + i = 0; + for (m = 0, len = value.length; m < len; m++) { + val = value[m]; + if (val > this.maxValue) { + if (this.options.limitMax) { + val = this.maxValue; + } else { + this.maxValue = val + 1; + } + } else if (val < this.minValue) { + if (this.options.limitMin) { + val = this.minValue; + } else { + this.minValue = val - 1; + } + } + this.gp[i].value = val; + this.gp[i++].setOptions({ + minValue: this.minValue, + maxValue: this.maxValue, + angle: this.options.angle + }); + } + this.value = Math.max(Math.min(value[value.length - 1], this.maxValue), this.minValue); + AnimationUpdater.add(this); + AnimationUpdater.run(this.forceUpdate); + return this.forceUpdate = false; + }; + + Gauge.prototype.getAngle = function (value) { + if (this.options.mirror) { + return 2 * Math.PI - ((value - this.minValue) / (this.maxValue - this.minValue)) * (this.options.range) * Math.PI; + } else { + return (1 + this.options.angle) * Math.PI + ((value - this.minValue) / (this.maxValue - this.minValue)) * (this.options.range) * Math.PI; + } + }; + + Gauge.prototype.getStartAngle = function () { + if (this.options.mirror) { + return (2 - this.options.angle) * Math.PI; + } else { + return (1 + this.options.angle) * Math.PI; + } + }; + + Gauge.prototype.getEndAngle = function () { + if (this.options.mirror) { + return (2 - this.options.angle - this.options.range) * Math.PI; + } else { + return (1 + this.options.angle + this.options.range) * Math.PI; + } + }; + + Gauge.prototype.getColorForPercentage = function (pct, grad) { + var color, endColor, i, j, rangePct, ref, startColor; + if (pct === 0) { + color = this.percentColors[0].color; + } else { + color = this.percentColors[this.percentColors.length - 1].color; + for (i = j = 0, ref = this.percentColors.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) { + if (pct <= this.percentColors[i].pct) { + if (grad === true) { + startColor = this.percentColors[i - 1] || this.percentColors[0]; + endColor = this.percentColors[i]; + rangePct = (pct - startColor.pct) / (endColor.pct - startColor.pct); + color = { + r: Math.floor(startColor.color.r * (1 - rangePct) + endColor.color.r * rangePct), + g: Math.floor(startColor.color.g * (1 - rangePct) + endColor.color.g * rangePct), + b: Math.floor(startColor.color.b * (1 - rangePct) + endColor.color.b * rangePct) + }; + } else { + color = this.percentColors[i].color; + } + break; + } + } + } + return 'rgb(' + [color.r, color.g, color.b].join(',') + ')'; + }; + + Gauge.prototype.getColorForValue = function (val, grad) { + var pct; + pct = (val - this.minValue) / (this.maxValue - this.minValue); + return this.getColorForPercentage(pct, grad); + }; + + Gauge.prototype.renderStaticLabels = function (staticLabels, w, h, radius) { + var font, fontsize, j, len, match, re, ref, rest, rotationAngle, value, textvalue, textstring, labelPosition; + this.ctx.save(); + this.ctx.translate(w, h); + font = staticLabels.font || "10px Times"; + re = /\d+\.?\d?/; + match = font.match(re)[0]; + rest = font.slice(match.length); + fontsize = parseFloat(match) * this.displayScale; + this.ctx.font = fontsize + rest; + this.ctx.fillStyle = staticLabels.color || "#000000"; + this.ctx.textBaseline = "bottom"; + this.ctx.textAlign = "center"; + ref = staticLabels.labels; + for (j = 0, len = ref.length; j < len; j++) { + value = ref[j]; + if (value.label !== void 0) { + font = value.font || staticLabels.font; + match = font.match(re)[0]; + rest = font.slice(match.length); + fontsize = parseFloat(match) * this.displayScale; + this.ctx.font = fontsize + rest; + textvalue = value.label; + } else { + textvalue = value; + } + if ((!this.options.limitMin || textvalue >= this.minValue) && (!this.options.limitMax || textvalue <= this.maxValue)) { + if ((textvalue == this.minValue && !this.options.mirror) || (textvalue == this.maxValue && this.options.mirror)) { + this.ctx.textAlign = "left"; + } else if ((textvalue == this.minValue && this.options.mirror) || (textvalue == this.maxValue && !this.options.mirror)) { + this.ctx.textAlign = "right"; + } + if (textvalue != 0) { + textvalue = formatNumber(value, staticLabels.fractionDigits); + } + if ( staticLabels.unit !== void 0) { + textstring = textvalue + " " + staticLabels.unit; + } else { + textstring = textvalue; + } + if (staticLabels.insideLabel) { + labelPosition = -radius + this.lineWidth / 2 + 6; + this.ctx.textBaseline = "top"; + } else { + labelPosition = -radius - this.lineWidth / 2; + this.ctx.textBaseline = "bottom"; + } + rotationAngle = this.getAngle(value) - 3 * Math.PI / 2; + this.ctx.rotate(rotationAngle); + this.ctx.fillText(textstring, 0, labelPosition); + this.ctx.rotate(-rotationAngle); + this.ctx.textAlign = "center"; + } + } + return this.ctx.restore(); + }; + + Gauge.prototype.renderTicks = function (ticksOptions, w, h, radius) { + var currentDivision, currentSubDivision, divColor, divLength, divWidth, divisionCount, j, lineWidth, range, rangeDivisions, ref, results, scaleMutate, st, subColor, subDivisions, subLength, subWidth, subdivisionCount, t, tmpRadius; + if (ticksOptions !== {}) { + divisionCount = ticksOptions.divisions || 0; + subdivisionCount = ticksOptions.subDivisions || 0; + divColor = ticksOptions.divColor || '#fff'; + subColor = ticksOptions.subColor || '#fff'; + divLength = ticksOptions.divLength || 0.7; + subLength = ticksOptions.subLength || 0.2; + range = parseFloat(this.maxValue) - parseFloat(this.minValue); + rangeDivisions = parseFloat(range) / parseFloat(ticksOptions.divisions); + subDivisions = parseFloat(rangeDivisions) / parseFloat(ticksOptions.subDivisions); + currentDivision = parseFloat(this.minValue); + currentSubDivision = 0.0 + subDivisions; + lineWidth = range / 400; + divWidth = lineWidth * (ticksOptions.divWidth || 1); + subWidth = lineWidth * (ticksOptions.subWidth || 1); + results = []; + for (t = j = 0, ref = divisionCount + 1; j < ref; t = j += 1) { + this.ctx.lineWidth = this.lineWidth * divLength; + scaleMutate = (this.lineWidth / 2) * (1 - divLength); + tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate; + this.ctx.strokeStyle = divColor; + this.ctx.beginPath(); + this.ctx.arc(0, 0, tmpRadius, this.getAngle(currentDivision - divWidth), this.getAngle(currentDivision + divWidth), false); + this.ctx.stroke(); + currentSubDivision = currentDivision + subDivisions; + currentDivision += rangeDivisions; + if (t !== ticksOptions.divisions && subdivisionCount > 0) { + results.push((function () { + var l, ref1, results1; + results1 = []; + for (st = l = 0, ref1 = subdivisionCount - 1; l < ref1; st = l += 1) { + this.ctx.lineWidth = this.lineWidth * subLength; + scaleMutate = (this.lineWidth / 2) * (1 - subLength); + tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate; + this.ctx.strokeStyle = subColor; + this.ctx.beginPath(); + this.ctx.arc(0, 0, tmpRadius, this.getAngle(currentSubDivision - subWidth), this.getAngle(currentSubDivision + subWidth), false); + this.ctx.stroke(); + results1.push(currentSubDivision += subDivisions); + } + return results1; + }).call(this)); + } else { + results.push(void 0); + } + } + return results; + } + }; + + Gauge.prototype.render = function () { + var displayedAngle, fillStyle, gauge, h, j, l, len, len1, max, min, radius, ref, ref1, scaleMutate, tmpRadius, w, zone; + w = this.canvas.width / 2; + h = (this.canvas.height * this.paddingTop + this.availableHeight) - ((this.radius + this.lineWidth / 2) * this.extraPadding); + displayedAngle = this.getAngle(this.displayedValue); + if (this.textField) { + this.textField.render(this); + } + this.ctx.lineCap = "butt"; + radius = this.radius * this.options.radiusScale; + if (this.options.staticLabels) { + this.renderStaticLabels(this.options.staticLabels, w, h, radius); + } + if (this.options.staticZones) { + this.ctx.save(); + this.ctx.translate(w, h); + this.ctx.lineWidth = this.lineWidth; + ref = this.options.staticZones; + for (j = 0, len = ref.length; j < len; j++) { + zone = ref[j]; + min = zone.min; + if (this.options.limitMin && min < this.minValue) { + min = this.minValue; + } + max = zone.max; + if (this.options.limitMax && max > this.maxValue) { + max = this.maxValue; + } + tmpRadius = this.radius * this.options.radiusScale; + if (zone.height) { + this.ctx.lineWidth = this.lineWidth * zone.height; + scaleMutate = (this.lineWidth / 2) * (zone.offset || 1 - zone.height); + tmpRadius = (this.radius * this.options.radiusScale) + scaleMutate; + } + this.ctx.strokeStyle = zone.strokeStyle; + this.ctx.beginPath(); + this.ctx.arc(0, 0, tmpRadius, this.getAngle(min), this.getAngle(max), false); + this.ctx.stroke(); + } + } else { + if (this.options.customFillStyle !== void 0) { + fillStyle = this.options.customFillStyle(this); + } else if (this.percentColors !== null) { + fillStyle = this.getColorForValue(this.displayedValue, this.options.generateGradient); + } else if (this.options.colorStop !== void 0) { + if (this.options.gradientType === 0) { + fillStyle = this.ctx.createRadialGradient(w, h, 9, w, h, 70); + } else { + fillStyle = this.ctx.createLinearGradient(0, 0, w, 0); + } + fillStyle.addColorStop(0, this.options.colorStart); + fillStyle.addColorStop(1, this.options.colorStop); + } else { + fillStyle = this.options.colorStart; + } + this.ctx.strokeStyle = fillStyle; + this.ctx.beginPath(); + this.ctx.arc(w, h, radius, this.getStartAngle(), displayedAngle, this.options.mirror); + this.ctx.lineWidth = this.lineWidth; + this.ctx.stroke(); + this.ctx.strokeStyle = this.options.strokeColor; + this.ctx.beginPath(); + this.ctx.arc(w, h, radius, displayedAngle, this.getEndAngle(), this.options.mirror); + this.ctx.stroke(); + this.ctx.save(); + this.ctx.translate(w, h); + } + if (this.options.renderTicks) { + this.renderTicks(this.options.renderTicks, w, h, radius); + } + this.ctx.restore(); + this.ctx.translate(w, h); + ref1 = this.gp; + for (l = 0, len1 = ref1.length; l < len1; l++) { + gauge = ref1[l]; + gauge.update(true); + } + return this.ctx.translate(-w, -h); + }; + + return Gauge; + + })(BaseGauge); + + BaseDonut = (function (superClass) { + extend(BaseDonut, superClass); + + BaseDonut.prototype.lineWidth = 15; + + BaseDonut.prototype.displayedValue = 0; + + BaseDonut.prototype.value = 33; + + BaseDonut.prototype.maxValue = 80; + + BaseDonut.prototype.minValue = 0; + + BaseDonut.prototype.options = { + lineWidth: 0.10, + colorStart: "#6f6ea0", + colorStop: "#c0c0db", + strokeColor: "#eeeeee", + shadowColor: "#d5d5d5", + angle: 0.35, + radiusScale: 1.0 + }; + + function BaseDonut(canvas) { + this.canvas = canvas; + BaseDonut.__super__.constructor.call(this); + if (typeof G_vmlCanvasManager !== 'undefined') { + this.canvas = window.G_vmlCanvasManager.initElement(this.canvas); + } + this.ctx = this.canvas.getContext('2d'); + this.setOptions(); + this.render(); + } + + BaseDonut.prototype.getAngle = function (value) { + return (1 - this.options.angle) * Math.PI + ((value - this.minValue) / (this.maxValue - this.minValue)) * ((2 + this.options.angle) - (1 - this.options.angle)) * Math.PI; + }; + + BaseDonut.prototype.setOptions = function (options) { + if (options == null) { + options = null; + } + BaseDonut.__super__.setOptions.call(this, options); + this.lineWidth = this.canvas.height * this.options.lineWidth; + this.radius = this.options.radiusScale * (this.canvas.height / 2 - this.lineWidth / 2); + return this; + }; + + BaseDonut.prototype.set = function (value) { + this.value = this.parseValue(value); + if (this.value > this.maxValue) { + if (this.options.limitMax) { + this.value = this.maxValue; + } else { + this.maxValue = this.value; + } + } else if (this.value < this.minValue) { + if (this.options.limitMin) { + this.value = this.minValue; + } else { + this.minValue = this.value; + } + } + AnimationUpdater.add(this); + AnimationUpdater.run(this.forceUpdate); + return this.forceUpdate = false; + }; + + BaseDonut.prototype.render = function () { + var displayedAngle, grdFill, h, start, stop, w; + displayedAngle = this.getAngle(this.displayedValue); + w = this.canvas.width / 2; + h = this.canvas.height / 2; + if (this.textField) { + this.textField.render(this); + } + grdFill = this.ctx.createRadialGradient(w, h, 39, w, h, 70); + grdFill.addColorStop(0, this.options.colorStart); + grdFill.addColorStop(1, this.options.colorStop); + start = this.radius - this.lineWidth / 2; + stop = this.radius + this.lineWidth / 2; + this.ctx.strokeStyle = this.options.strokeColor; + this.ctx.beginPath(); + this.ctx.arc(w, h, this.radius, (1 - this.options.angle) * Math.PI, (2 + this.options.angle) * Math.PI, false); + this.ctx.lineWidth = this.lineWidth; + this.ctx.lineCap = "round"; + this.ctx.stroke(); + this.ctx.strokeStyle = grdFill; + this.ctx.beginPath(); + this.ctx.arc(w, h, this.radius, (1 - this.options.angle) * Math.PI, displayedAngle, false); + return this.ctx.stroke(); + }; + + return BaseDonut; + + })(BaseGauge); + + Donut = (function (superClass) { + extend(Donut, superClass); + + function Donut() { + return Donut.__super__.constructor.apply(this, arguments); + } + + Donut.prototype.strokeGradient = function (w, h, start, stop) { + var grd; + grd = this.ctx.createRadialGradient(w, h, start, w, h, stop); + grd.addColorStop(0, this.options.shadowColor); + grd.addColorStop(0.12, this.options._orgStrokeColor); + grd.addColorStop(0.88, this.options._orgStrokeColor); + grd.addColorStop(1, this.options.shadowColor); + return grd; + }; + + Donut.prototype.setOptions = function (options) { + var h, start, stop, w; + if (options == null) { + options = null; + } + Donut.__super__.setOptions.call(this, options); + w = this.canvas.width / 2; + h = this.canvas.height / 2; + start = this.radius - this.lineWidth / 2; + stop = this.radius + this.lineWidth / 2; + this.options._orgStrokeColor = this.options.strokeColor; + this.options.strokeColor = this.strokeGradient(w, h, start, stop); + return this; + }; + + return Donut; + + })(BaseDonut); + + window.AnimationUpdater = { + elements: [], + animId: null, + addAll: function (list) { + var elem, j, len, results; + results = []; + for (j = 0, len = list.length; j < len; j++) { + elem = list[j]; + results.push(AnimationUpdater.elements.push(elem)); + } + return results; + }, + add: function (object) { + if (indexOf.call(AnimationUpdater.elements, object) < 0) { + return AnimationUpdater.elements.push(object); + } + }, + run: function (force) { + var elem, finished, isCallback, j, k, l, len, ref, toRemove; + if (force == null) { + force = false; + } + isCallback = isFinite(parseFloat(force)); + if (isCallback || force === true) { + finished = true; + toRemove = []; + ref = AnimationUpdater.elements; + for (k = j = 0, len = ref.length; j < len; k = ++j) { + elem = ref[k]; + if (elem.update(force === true)) { + finished = false; + } else { + toRemove.push(k); + } + } + for (l = toRemove.length - 1; l >= 0; l += -1) { + k = toRemove[l]; + AnimationUpdater.elements.splice(k, 1); + } + return AnimationUpdater.animId = finished ? null : requestAnimationFrame(AnimationUpdater.run); + } else if (force === false) { + if (AnimationUpdater.animId === !null) { + cancelAnimationFrame(AnimationUpdater.animId); + } + return AnimationUpdater.animId = requestAnimationFrame(AnimationUpdater.run); + } + } + }; + + if (typeof window.define === 'function' && (window.define.amd != null)) { + define(function () { + return { + Gauge: Gauge, + Donut: Donut, + BaseDonut: BaseDonut, + TextRenderer: TextRenderer + }; + }); + } else if (typeof module !== 'undefined' && (module.exports != null)) { + module.exports = { + Gauge: Gauge, + Donut: Donut, + BaseDonut: BaseDonut, + TextRenderer: TextRenderer + }; + } else { + window.Gauge = Gauge; + window.Donut = Donut; + window.BaseDonut = BaseDonut; + window.TextRenderer = TextRenderer; + } + +}).call(this); + +//# sourceMappingURL=gauge.js.map diff --git a/web/display/minimal/gauge.min.js b/web/display/minimal/gauge.min.js deleted file mode 100644 index 1b77c1c27b..0000000000 --- a/web/display/minimal/gauge.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(){function t(t,i){for(var e in i)m.call(i,e)&&(t[e]=i[e]);function s(){this.constructor=t}return s.prototype=i.prototype,t.prototype=new s,t.__super__=i.prototype,t}var i,e,s,n,o,p,a,h,r,l,g,c,u,d=[].slice,m={}.hasOwnProperty,x=[].indexOf||function(t){for(var i=0,e=this.length;ithis.gp.length)for(e=n=0,r=t.length-this.gp.length;0<=r?nthis.maxValue?this.options.limitMax?l=this.maxValue:this.maxValue=l+1:l=this.minValue)&&(!this.options.limitMax||d<=this.maxValue)&&(r=(n=d.font||t.font).match(l)[0],c=n.slice(r.length),o=parseFloat(r)*this.displayScale,this.ctx.font=o+c,u=this.getAngle(d.label)-3*Math.PI/2,this.ctx.rotate(u),this.ctx.fillText(g(d.label,t.fractionDigits),0,-s-this.lineWidth/2),this.ctx.rotate(-u)):(!this.options.limitMin||d>=this.minValue)&&(!this.options.limitMax||d<=this.maxValue)&&(u=this.getAngle(d)-3*Math.PI/2,this.ctx.rotate(u),this.ctx.fillText(g(d,t.fractionDigits),0,-s-this.lineWidth/2),this.ctx.rotate(-u));return this.ctx.restore()},M.prototype.renderTicks=function(t,i,e,s){var n,o,a,h,r,l,p,c,u,d,g,m,x,f,v,y,V,w,S,M;if(t!=={}){for(l=t.divisions||0,w=t.subDivisions||0,a=t.divColor||"#fff",f=t.subColor||"#fff",h=t.divLength||.7,y=t.subLength||.2,u=parseFloat(this.maxValue)-parseFloat(this.minValue),d=parseFloat(u)/parseFloat(t.divisions),v=parseFloat(d)/parseFloat(t.subDivisions),n=parseFloat(this.minValue),o=0+v,r=(c=u/400)*(t.divWidth||1),V=c*(t.subWidth||1),m=[],S=p=0,g=l+1;pthis.maxValue&&(h=this.maxValue),d=this.radius*this.options.radiusScale,m.height&&(this.ctx.lineWidth=this.lineWidth*m.height,u=this.lineWidth/2*(m.offset||1-m.height),d=this.radius*this.options.radiusScale+u),this.ctx.strokeStyle=m.strokeStyle,this.ctx.beginPath(),this.ctx.arc(0,0,d,this.getAngle(r),this.getAngle(h),!1),this.ctx.stroke();else void 0!==this.options.customFillStyle?i=this.options.customFillStyle(this):null!==this.percentColors?i=this.getColorForValue(this.displayedValue,this.options.generateGradient):void 0!==this.options.colorStop?((i=0===this.options.gradientType?this.ctx.createRadialGradient(g,e,9,g,e,70):this.ctx.createLinearGradient(0,0,g,0)).addColorStop(0,this.options.colorStart),i.addColorStop(1,this.options.colorStop)):i=this.options.colorStart,this.ctx.strokeStyle=i,this.ctx.beginPath(),this.ctx.arc(g,e,l,(1+this.options.angle)*Math.PI,t,!1),this.ctx.lineWidth=this.lineWidth,this.ctx.stroke(),this.ctx.strokeStyle=this.options.strokeColor,this.ctx.beginPath(),this.ctx.arc(g,e,l,t,(2-this.options.angle)*Math.PI,!1),this.ctx.stroke(),this.ctx.save(),this.ctx.translate(g,e);for(this.options.renderTicks&&this.renderTicks(this.options.renderTicks,g,e,l),this.ctx.restore(),this.ctx.translate(g,e),n=0,a=(c=this.gp).length;nthis.maxValue?this.options.limitMax?this.value=this.maxValue:this.maxValue=this.value:this.value + diff --git a/web/display/minimal/index.php b/web/display/minimal/index.php index 303638bc45..c6eec14947 100644 --- a/web/display/minimal/index.php +++ b/web/display/minimal/index.php @@ -1,5 +1,6 @@ + @@ -19,13 +20,13 @@ + - - + +
-
- -
0
- -
0
-
- - - + + +
+ + +
LP1
+
0
+
0
+
+ + + +
-
+
+ + +
LP2
+
0
+
0
+
+ + + +
+
+ +
+ + +
0
+
0
+
+ + + +
+
+
0) { ?>
@@ -100,18 +130,18 @@
@@ -123,40 +153,37 @@ function startTime() { var h = today.getHours(); var m = today.getMinutes(); m = checkTime(m); - document.getElementById('theclock').innerHTML = - h + ":" + m; + document.getElementById('theclock').innerHTML = h + ":" + m; var t = setTimeout(startTime, 5000); } function checkTime(i) { if (i < 10) { i = "0" + i - }; // add zero in front of numbers < 10 + }; // add zero in front of numbers < 10 return i; } startTime(); var displaylp1max = ; - // var displaylp2max = ; - var displaypincode = ; - var displaypinaktiv = ; + var displaylp2max = ; var rfidakt = ; - var isssakt = ; + var lastmanagementold = ; - + + diff --git a/web/display/minimal/minimalgauge.js b/web/display/minimal/minimalgauge.js index 28847f5434..bb7cef8b03 100644 --- a/web/display/minimal/minimalgauge.js +++ b/web/display/minimal/minimalgauge.js @@ -2,33 +2,55 @@ * LP1 gauge */ var opts = { - angle: 0, // The span of the gauge arc - lineWidth: 0.24, // The line thickness - radiusScale: 0.67, // Relative radius + lineWidth: 0.30, // The line thickness + angle: 0, // The angle where gauge starts + radiusScale: 1, // Relative radius pointer: { length: 0.6, // // Relative to gauge radius strokeWidth: 0, // The thickness color: '#000000' // Fill color }, - limitMax: false, // If false, max value increases automatically if value > maxValue + limitMax: true, // If false, max value increases automatically if value > maxValue limitMin: true, // If true, the min value of the gauge will be fixed colorStart: '#6FADCF', // Colors colorStop: '#8FC0DA', // just experiment with them strokeColor: 'grey', // to see which ones work best for you generateGradient: true, - percentColors: [[0.0, "#73c0FF" ], [1.0, "#0000FF"]], - staticLabels: { + percentColors: [[0.0, "#73c0FF"], [1.0, "#0000FF"]], +}; + +// Duo will only use a quarter circle +if (lastmanagementold != '1') { + opts['range'] = 1; // The span of the gauge arc +} else { + opts['range'] = 0.5; // The span of the gauge arc +} + +// switch range depending on magnitute of max value +if (displaylp1max > 999) { + var gaugeLp1MaxValue = displaylp1max / 1000; + opts['staticLabels'] = { font: "13px sans-serif", // Specifies font - labels: [0, displaylp1max], // Print labels at these values + labels: [0, gaugeLp1MaxValue], // Print labels at these values + unit: "kW", // Optional: Label unit + color: "white", // Optional: Label text color + fractionDigits: 2 // Optional: Numerical precision. 0=round off. + } +} else { + var gaugeLp1MaxValue = displaylp1max; + opts['staticLabels'] = { + font: "13px sans-serif", // Specifies font + labels: [0, gaugeLp1MaxValue], // Print labels at these values + unit: "W", // Optional: Label unit color: "white", // Optional: Label text color fractionDigits: 0 // Optional: Numerical precision. 0=round off. - }, -}; + } +} var targetlp1 = document.getElementById('lp1'); // your canvas element var gaugelp1 = new Gauge(targetlp1).setOptions(opts); // create sexy gauge! -gaugelp1.maxValue = displaylp1max; // set max gauge value +gaugelp1.maxValue = gaugeLp1MaxValue; // set max gauge value gaugelp1.setMinValue(0); // Prefer setter over gauge.minValue = 0 gaugelp1.animationSpeed = 5; // set animation speed (32 is default value) gaugelp1.set(0); // set actual value @@ -37,29 +59,38 @@ gaugelp1.set(0); // set actual value * LP1-SoC gauge */ var opts = { - angle: 0, // The span of the gauge arc - lineWidth: 0.08, // The line thickness - radiusScale: 0.5, // Relative radius + lineWidth: 0.10, // The line thickness + radiusScale: 0.66, // Relative radius + angle: 0, // The angle where gauge starts pointer: { length: 0.6, // // Relative to gauge radius strokeWidth: 0, // The thickness color: '#000000' // Fill color }, - limitMax: false, // If false, max value increases automatically if value > maxValue - limitMin: false, // If true, the min value of the gauge will be fixed + limitMax: true, // If false, max value increases automatically if value > maxValue + limitMin: true, // If true, the min value of the gauge will be fixed colorStart: '#6FADCF', // Colors colorStop: '#8FC0DA', // just experiment with them strokeColor: 'grey', // to see which ones work best for you generateGradient: true, - percentColors: [[0.0, "#73c0FF" ], [1.0, "#0000FF"]], staticLabels: { font: "13px sans-serif", // Specifies font labels: [0, 100], // Print labels at these values + unit: "%", // Optional: Label unit + insideLabel: true, // Optional: Labels are printed insde of the gauge color: "white", // Optional: Label text color fractionDigits: 0 // Optional: Numerical precision. 0=round off. - }, + } }; +// Duo will only use a quarter circle +if (lastmanagementold != '1') { + opts['range'] = 1; // The span of the gauge arc + opts['percentColors'] = [[0.0, "#73c0FF"], [1.0, "#0000FF"]]; +} else { + opts['range'] = 0.5; // The span of the gauge arc + opts['percentColors'] = [[0.0, "#B22222"], [0.5, "#DAA520"], [1.0, "#33A02C"]]; +} var targetlp1s = document.getElementById('lp1s'); // your canvas element var gaugelp1s = new Gauge(targetlp1s).setOptions(opts); // create sexy gauge! @@ -68,72 +99,95 @@ gaugelp1s.setMinValue(0); // Prefer setter over gauge.minValue = 0 gaugelp1s.animationSpeed = 5; // set animation speed (32 is default value) gaugelp1s.set(70); // set actual value -/*********** - * LP2 gauge - */ -// var opts = { -// angle: 0, // The span of the gauge arc -// lineWidth: 0.24, // The line thickness -// radiusScale: 0.67, // Relative radius -// pointer: { -// length: 0.6, // // Relative to gauge radius -// strokeWidth: 0, // The thickness -// color: '#000000' // Fill color -// }, -// limitMax: false, // If false, max value increases automatically if value > maxValue -// limitMin: true, // If true, the min value of the gauge will be fixed -// colorStart: '#6FADCF', // Colors -// colorStop: '#8FC0DA', // just experiment with them -// strokeColor: 'grey', // to see which ones work best for you -// generateGradient: true, -// percentColors: [[0.0, "#73c0FF" ], [1.0, "#0000FF"]], -// staticLabels: { -// font: "13px sans-serif", // Specifies font -// labels: [0, displaylp2max], // Print labels at these values -// color: "white", // Optional: Label text color -// fractionDigits: 0 // Optional: Numerical precision. 0=round off. -// }, -// }; -// var targetlp2 = document.getElementById('lp2'); // your canvas element -// var gaugelp2 = new Gauge(targetlp2).setOptions(opts); // create sexy gauge! +if (lastmanagementold == '1') { + /*********** + * LP2 gauge + */ + var opts = { + angle: 0, // The angle where gauge starts + range: 0.5, // The span of the gauge arc + mirror: true, // start gauge from the right + lineWidth: 0.30, // The line thickness + radiusScale: 1, // Relative radius + pointer: { + length: 0.6, // // Relative to gauge radius + strokeWidth: 0, // The thickness + color: '#000000' // Fill color + }, + limitMax: true, // If false, max value increases automatically if value > maxValue + limitMin: true, // If true, the min value of the gauge will be fixed + colorStart: '#6FADCF', // Colors + colorStop: '#8FC0DA', // just experiment with them + strokeColor: 'grey', // to see which ones work best for you + generateGradient: true, + percentColors: [[0.0, "#73c0FF"], [1.0, "#0000FF"]], + }; -// gaugelp2.maxValue = displaylp2max; // set max gauge value -// gaugelp2.setMinValue(0); // Prefer setter over gauge.minValue = 0 -// gaugelp2.animationSpeed = 5; // set animation speed (32 is default value) -// gaugelp2.set(0); // set actual value + // switch range depending on magnitute of max value + if (displaylp2max > 999) { + var gaugeLp2MaxValue = displaylp2max / 1000; + opts['staticLabels'] = { + font: "13px sans-serif", // Specifies font + labels: [0, gaugeLp2MaxValue], // Print labels at these values + unit: "kW", // Optional: Label unit + color: "white", // Optional: Label text color + fractionDigits: 2 // Optional: Numerical precision. 0=round off. + } + } else { + var gaugeLp2MaxValue = displaylp2max; + opts['staticLabels'] = { + font: "13px sans-serif", // Specifies font + labels: [0, gaugeLp2MaxValue], // Print labels at these values + unit: "W", // Optional: Label unit + color: "white", // Optional: Label text color + fractionDigits: 0 // Optional: Numerical precision. 0=round off. + } + } -/*********** - * LP2-SoC gauge - */ -// var opts = { -// angle: 0, // The span of the gauge arc -// lineWidth: 0.08, // The line thickness -// radiusScale: 0.5, // Relative radius -// pointer: { -// length: 0.6, // // Relative to gauge radius -// strokeWidth: 0, // The thickness -// color: '#000000' // Fill color -// }, -// limitMax: false, // If false, max value increases automatically if value > maxValue -// limitMin: false, // If true, the min value of the gauge will be fixed -// colorStart: '#6FADCF', // Colors -// colorStop: '#8FC0DA', // just experiment with them -// strokeColor: 'grey', // to see which ones work best for you -// generateGradient: true, -// percentColors: [[0.0, "#73c0FF" ], [1.0, "#0000FF"]], -// staticLabels: { -// font: "13px sans-serif", // Specifies font -// labels: [0, 100], // Print labels at these values -// color: "white", // Optional: Label text color -// fractionDigits: 0 // Optional: Numerical precision. 0=round off. -// }, -// }; -// var targetlp2s = document.getElementById('lp2s'); // your canvas element -// var gaugelp2s = new Gauge(targetlp2s).setOptions(opts); // create sexy gauge! + var targetlp2 = document.getElementById('lp2'); // your canvas element + var gaugelp2 = new Gauge(targetlp2).setOptions(opts); // create sexy gauge! + + gaugelp2.maxValue = gaugeLp2MaxValue; // set max gauge value + gaugelp2.setMinValue(0); // Prefer setter over gauge.minValue = 0 + gaugelp2.animationSpeed = 5; // set animation speed (32 is default value) + gaugelp2.set(0); // set actual value -// gaugelp2s.maxValue = 100; // set max gauge value -// gaugelp2s.setMinValue(0); // Prefer setter over gauge.minValue = 0 -// gaugelp2s.animationSpeed = 5; // set animation speed (32 is default value) -// gaugelp2s.set(70); // set actual value + /*********** + * LP2-SoC gauge + */ + var opts = { + angle: 0, // The angle where gauge starts + range: 0.5, // The span of the gauge arc + mirror: true, // start gauge from the right + lineWidth: 0.1, // The line thickness + radiusScale: 0.66, // Relative radius + pointer: { + length: 0.6, // // Relative to gauge radius + strokeWidth: 0, // The thickness + color: '#000000' // Fill color + }, + limitMax: true, // If false, max value increases automatically if value > maxValue + limitMin: true, // If true, the min value of the gauge will be fixed + colorStart: '#6FADCF', // Colors + colorStop: '#8FC0DA', // just experiment with them + strokeColor: 'grey', // to see which ones work best for you + generateGradient: true, + percentColors: [[0.0, "#B22222"], [0.5, "#DAA520"], [1.0, "#33A02C"]], + staticLabels: { + font: "13px sans-serif", // Specifies font + labels: [0, 100], // Print labels at these values + unit: "%", // Optional: Label unit + insideLabel: true, // Optional: Labels are printed insde of the gauge + color: "white", // Optional: Label text color + fractionDigits: 0 // Optional: Numerical precision. 0=round off. + } + }; + var targetlp2s = document.getElementById('lp2s'); // your canvas element + var gaugelp2s = new Gauge(targetlp2s).setOptions(opts); // create sexy gauge! + gaugelp2s.maxValue = 100; // set max gauge value + gaugelp2s.setMinValue(0); // Prefer setter over gauge.minValue = 0 + gaugelp2s.animationSpeed = 5; // set animation speed (32 is default value) + gaugelp2s.set(70); // set actual value +} diff --git a/web/display/minimal/processAllMqttMsg.js b/web/display/minimal/processAllMqttMsg.js index aa972703c4..f51265032f 100644 --- a/web/display/minimal/processAllMqttMsg.js +++ b/web/display/minimal/processAllMqttMsg.js @@ -12,10 +12,10 @@ function reloadDisplay() { */ // wait some seconds to allow other instances receive this message console.log("reloading display..."); - setTimeout(function(){ - publish( "0", "openWB/set/system/reloadDisplay" ); + setTimeout(function () { + publish("0", "openWB/set/system/reloadDisplay"); // wait again to give the broker some time and avoid a reload loop - setTimeout(function(){ + setTimeout(function () { location.reload(); }, 2000); }, 2000); @@ -24,82 +24,72 @@ function reloadDisplay() { function handlevar(mqttmsg, mqttpayload) { // console.log("Topic: "+mqttmsg+" Message: "+mqttpayload); // receives all messages and calls respective function to process them - if ( mqttmsg.match( /^openwb\/system\//i) ) { processSystemMessages(mqttmsg, mqttpayload); } - else if ( mqttmsg.match( /^openwb\/lp\//i) ) { processChargepointMessages(mqttmsg, mqttpayload); } + if (mqttmsg.match(/^openwb\/system\//i)) { processSystemMessages(mqttmsg, mqttpayload); } + else if (mqttmsg.match(/^openwb\/lp\//i)) { processChargepointMessages(mqttmsg, mqttpayload); } } // end handlevar function processSystemMessages(mqttmsg, mqttpayload) { // processes mqttmsg for topic openWB/system // called by handlevar - if ( mqttmsg == 'openWB/system/reloadDisplay' ) { - if( mqttpayload == '1' ){ + if (mqttmsg == 'openWB/system/reloadDisplay') { + if (mqttpayload == '1') { reloadDisplay(); } } - // else if ( mqttmsg == 'openWB/system/parentWB' ) { - // console.log("received parent openwb url: " + mqttpayload); - // } - // else if ( mqttmsg == 'openWB/system/parentCPlp1' ) { - // console.log("received index for lp1 on parent openwb: " + mqttpayload); - // } - // else if ( mqttmsg == 'openWB/system/parentCPlp2' ) { - // console.log("received index for lp2 on parent openwb: " + mqttpayload); - // } } function processChargepointMessages(mqttmsg, mqttpayload) { // processes mqttmsg for topic openWB/system // called by handlevar - if ( mqttmsg == 'openWB/lp/2/boolChargePointConfigured' ) { - console.log("received configured for local lp2: " + mqttpayload) - } - else if ( mqttmsg == 'openWB/lp/1/%Soc' ) { - var lp1s = mqttpayload; - gaugelp1s.set(lp1s); - $("#lp1st").html(mqttpayload + "%"); - } - else if ( mqttmsg == 'openWB/lp/1/W' ) { - var lp1w = mqttpayload; - gaugelp1.set(lp1w); - if ( lp1w > 999 ) { - lp1w= lp1w / 1000; - $("#lp1t").html(lp1w.toFixed(2) + " kW"); - } else { - $("#lp1t").html(mqttpayload + " W"); - } - } - else if (mqttmsg == 'openWB/lp/1/boolSocConfigured' ) { - if ( mqttpayload == 1 ) { - $("#lp1s").removeClass("hide"); - $("#lp1st").removeClass("hide"); - } else { - $("#lp1s").addClass("hide"); - $("#lp1st").addClass("hide"); - } - } - else if ( mqttmsg == 'openWB/lp/1/boolPlugStat' ) { - if ( mqttpayload == 1 ) { - $("#lp1plugstat").removeClass('hide'); - } else { - $("#lp1plugstat").addClass('hide'); - } - } - else if ( mqttmsg == 'openWB/lp/1/boolChargeStat' ) { - if ( mqttpayload == 1 ) { - $("#lp1plugstat").removeClass('text-warning').addClass('text-success'); - } else { - $("#lp1plugstat").removeClass('text-success').addClass('text-warning'); - } - } - else if ( mqttmsg == 'openWB/lp/1/ChargePointEnabled' ) { - var lp1enabled = mqttpayload; - if(lp1enabled == 1){ - $("#lp1enabled").removeClass("hide"); - $("#lp1disabled").addClass("hide"); - } else { - $("#lp1enabled").addClass("hide"); - $("#lp1disabled").removeClass("hide"); + + // check if topic contains subgroup like /lp/1/ + // last part of topic after / + var topic = mqttmsg.substring(mqttmsg.lastIndexOf('/') + 1); + var index = mqttmsg.match(/(\w+)\/(\d\d?)\//)[2]; + if (index != null) { + if (topic == '%Soc') { + eval("gaugelp" + index + "s.set(mqttpayload)"); + $('#lp' + index + 'st').html(mqttpayload + "%"); + } else if (topic == 'W') { + var lpw = mqttpayload / 1000; + if (eval("displaylp" + index + "max") > 999) { + eval("gaugelp" + index + ".set(lpw)"); + } else { + eval("gaugelp" + index + ".set(mqttpayload)"); + } + if (mqttpayload > 999) { + $('#lp' + index + 't').html(lpw.toFixed(2) + " kW"); + } else { + $('#lp' + index + 't').html(mqttpayload + " W"); + } + } else if (topic == 'boolSocConfigured') { + if (mqttpayload == 1) { + $('#lp' + index + 's').removeClass("hide"); + $('#lp' + index + 'st').removeClass("hide"); + } else { + $('#lp' + index + 's').addClass("hide"); + $('#lp' + index + 'st').addClass("hide"); + } + } else if (topic == 'boolPlugStat') { + if (mqttpayload == 1) { + $('#lp' + index + 'plugstat').removeClass('hide'); + } else { + $('#lp' + index + 'plugstat').addClass('hide'); + } + } else if (topic == 'boolChargeStat') { + if (mqttpayload == 1) { + $('#lp' + index + 'plugstat').removeClass('text-warning').addClass('text-success'); + } else { + $('#lp' + index + 'plugstat').removeClass('text-success').addClass('text-warning'); + } + } else if (topic == 'ChargePointEnabled') { + if (mqttpayload == 1) { + $('#lp' + index + 'enabled').removeClass("hide"); + $('#lp' + index + 'disabled').addClass("hide"); + } else { + $('#lp' + index + 'enabled').addClass("hide"); + $('#lp' + index + 'disabled').removeClass("hide"); + } } } - } diff --git a/web/display/minimal/setupMqttServices.js b/web/display/minimal/setupMqttServices.js index 171ce6f6fa..e4d674f87a 100644 --- a/web/display/minimal/setupMqttServices.js +++ b/web/display/minimal/setupMqttServices.js @@ -17,15 +17,12 @@ var topicsToSubscribe = [ ["openWB/lp/1/boolPlugStat", 1], ["openWB/lp/1/boolChargeStat", 1], ["openWB/lp/1/ChargePointEnabled", 1], - // ["openWB/lp/2/W", 1], - // ["openWB/lp/2/%Soc", 1], - // ["openWB/lp/2/boolSocConfigured"], - // ["openWB/lp/2/boolPlugStat", 1], - // ["openWB/lp/2/ChargePointEnabled", 1], - // ["openWB/system/parentWB", 1], - // ["openWB/system/parentCPlp1", 1], - // ["openWB/system/parentCPlp2", 1], - // ["openWB/lp/2/boolChargePointConfigured", 1] + ["openWB/lp/2/W", 1], + ["openWB/lp/2/%Soc", 1], + ["openWB/lp/2/boolSocConfigured"], + ["openWB/lp/2/boolPlugStat", 1], + ["openWB/lp/2/ChargePointEnabled", 1], + ["openWB/lp/2/boolChargeStat", 1], ]; // holds number of topics flagged 1 initially diff --git a/web/settings/misc.php b/web/settings/misc.php index b8ec71dcae..d46e192337 100644 --- a/web/settings/misc.php +++ b/web/settings/misc.php @@ -1274,16 +1274,18 @@ function visibility_ledsakt() {

-
+
">
- + + + + + + +
@@ -1327,59 +1329,19 @@ function visibility_ledsakt() {
-
- -
- -
-
-
- -
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- + +
class="hide"> +
+ +
+ +
-
+ +
-
@@ -1432,15 +1394,33 @@ function visibility_displaytheme() { switch ($('#displaytheme').val()) { case '0': // Cards showSection('#displaygauge'); - showSection('#displaycards'); + for (let cp = 1; cp < 9; cp++) { + showSection('#displaylp' + cp); + } + break; + case '2': // Minimal + hideSection('#displaygauge'); + for (let cp = 1; cp < 3; cp++) { + showSection('#displaylp' + cp); + } + for (let cp = 3; cp < 9; cp++) { + hideSection('#displaylp' + cp); + } break; case '3': // Gauges showSection('#displaygauge'); - hideSection('#displaycards'); + for (let cp = 1; cp < 3; cp++) { + showSection('#displaylp' + cp); + } + for (let cp = 3; cp < 9; cp++) { + hideSection('#displaylp' + cp); + } break; default: - hideSection('#displaygauge'); - hideSection('#displaycards'); + hideSection('#displaygauge'); + for (let cp = 1; cp < 9; cp++) { + hideSection('#displaylp' + cp); + } } } diff --git a/web/settings/settings.php b/web/settings/settings.php index 28814003f8..069987c59c 100644 --- a/web/settings/settings.php +++ b/web/settings/settings.php @@ -81,7 +81,7 @@ Wird hier Ja gewählt ist diese openWB nur ein Ladepunkt und übernimmt keine eigene Regelung. Hier ist Ja zu wählen wenn, bereits eine openWB vorhanden ist und diese nur ein weiterer Ladepunkt der vorhandenen openWB sein soll.
Es ist sicherzustellen, dass auf dieser openWB die Modulkonfiguration des ersten Ladepunktes - korrekt ist und alle weiteren Ladepunkte deaktiviert sind. Handels es sich hier um eine + korrekt ist und alle weiteren Ladepunkte deaktiviert sind. Handelt es sich hier um eine DUO, so ist auch der zweite Ladepunkt zu aktivieren.
Alle weiteren in dieser openWB getätigten Einstellungen werden NICHT beachtet. An der Haupt openWB wird als Ladepunkt "externe openWB" gewählt und die IP Adresse eingetragen. @@ -95,6 +95,9 @@ + + Für das Theme "Normal" kann der Maximalwert der Skala im Bereich "Display" unter Einstellungen->Verschiedenes angepasst werden. +
diff --git a/web/version b/web/version index cbf5776325..68d05d0d27 100644 --- a/web/version +++ b/web/version @@ -1 +1 @@ -1.9.289 +1.9.291