Exemple #1
0
    def __init__(self):
        super().__init__()
        hybridlogger.ha_log(self.logger, self.hass_api, "INFO",
                            "Client enabled: TCPclient")

        # Get plant_id_list
        self.plant_id_list = self.config.getlist("client.tcpclient",
                                                 "plant_id_list",
                                                 fallback=[])
        if not self.plant_id_list:
            hybridlogger.ha_log(
                self.logger,
                self.hass_api,
                "ERROR",
                "plant_id_list was not specified for client TCPclient",
            )
            raise Exception("plant_id_list was not specified")
        # Initialize dict with inverter info
        self.inverters = {}
        self.session = {}

        # self._mode indicates the access mode that will be used
        # 0: Not initialized
        # 1: Running in native mode (port 8899)
        # 2: Running in fallback mode (port 80) fetching http://{inverter_ip}:80/js/status.js
        self._mode = {}
Exemple #2
0
    def getPlantData(self, plant_id):
        data = None
        http_only = self.inverters[plant_id].get("http_only")
        if not self._mode.get(plant_id) and not http_only:
            # native mode
            hybridlogger.ha_log(
                self.logger,
                self.hass_api,
                "INFO",
                f"Initializing: Trying to reach the inverter for plant {plant_id} over port 8899.",
            )
        if (self._mode.get(plant_id) <= 1
                and self.inverters[plant_id].get("inverter_sn")
                and self.inverters[plant_id].get("logger_sn")
                and not http_only):
            data = self._getPlantData_native(plant_id)
        if data:
            # set mode to native
            self._mode[plant_id] = 1
        if not self._mode:
            hybridlogger.ha_log(
                self.logger,
                self.hass_api,
                "INFO",
                f"Initializing: Trying to reach the inverter for plant {plant_id} over port http (fallback).",
            )
        if self._mode.get(plant_id) == 0 or self._mode.get(plant_id) == 2:
            # fall back mode
            data = self._getPlantData_fallback(plant_id)

        return data
Exemple #3
0
 def _run(self, entity, attribute, old, new, kwargs):
     # HASSAPI callback handler
     hybridlogger.ha_log(
         self.logger,
         self.hass_api,
         "DEBUG",
         f"HASSapi state change for {self.hass_api.get_state(entity, 'inverter', 'n/a')} "
         f"at {self.hass_api.get_state(entity, 'last_update', 'n/a')}",
     )
     # Try to parse payload as json
     try:
         data = binascii.a2b_base64(new)
         if len(data) == 128:
             # Fill data structure
             self.client.semaphore.acquire()
             self.client.msg["data"] = data
             self.client.msg["isSet"] = True
             self.client.msg["plugin"] = __name__
             self.client.semaphore.release()
             # Trigger processing the message
             self.client.msgevent.set()
     except Exception as e:
         hybridlogger.ha_log(
             self.logger,
             self.hass_api,
             "WARNING",
             f"HASSapi: invalid data received: {new}. Error {e}",
         )
Exemple #4
0
 def __init__(self):
     super().__init__()
     hybridlogger.ha_log(self.logger, self.hass_api, "INFO",
                         "localproxy client plugin: MQTTproxy")
     self.logger_sensor_name = self.mqttconfig('logger_sensor_name',
                                               'Datalogger')
     self.mqtt_discovery_prefix = self.mqttconfig('listen_address',
                                                  'homeassistant')
     self.mqtt_host = self.mqttconfig('host', 'localhost')
     self.mqtt_port = int(self.mqttconfig('port', '1883'))
     self.mqtt_client_name_prefix = self.mqttconfig(
         'client_name_prefix', 'ha-mqttproxy-omniklogger')
     self.mqtt_client_name = self.mqtt_client_name_prefix + "_" + uuid.uuid4(
     ).hex
     self.mqtt_username = self.mqttconfig('username', None)
     self.mqtt_password = self.mqttconfig('password', None)
     if not self.mqtt_username or not self.mqtt_password:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "ERROR",
             "Please specify MQTT username and password in the configuration"
         )
     # mqtt setup
     self.mqtt_client = mqttclient.Client(self.mqtt_client_name)
     self.mqtt_client.on_connect = self._mqtt_on_connect  # bind call back function
     self.mqtt_client.on_disconnect = self._mqtt_on_disconnect  # bind call back function
     self.mqtt_client.on_message = self._mqtt_on_message  # called on receiving updates on subscibed messages
     self.mqtt_client.username_pw_set(self.mqtt_username,
                                      self.mqtt_password)
Exemple #5
0
    def get_weather(self):
        try:
            if "weather" not in self.cache:
                self.logger.debug("[cache miss] Fetching weather data")
                url = "https://{endpoint}/data/2.5/weather?lon={lon}&lat={lat}&units={units}&APPID={api_key}".format(
                    endpoint=self.config.get(
                        "openweathermap",
                        "endpoint",
                        fallback="api.openweathermap.org"),
                    lat=self.config.get("openweathermap", "lat"),
                    lon=self.config.get("openweathermap", "lon"),
                    units=self.config.get("openweathermap",
                                          "units",
                                          fallback="metric"),
                    api_key=self.config.get("openweathermap", "api_key"),
                )

                res = requests.get(url)

                res.raise_for_status()

                self.cache["weather"] = res.json()

            return self.cache["weather"]

        except requests.exceptions.HTTPError as e:
            hybridlogger.ha_log(
                self.logger,
                self.hass_api,
                "ERROR",
                "Unable to get weather data. [{0}]: {1}".format(
                    type(e).__name__, str(e)),
            )
            raise e
 def _validate_user_login(self):
     self.omnik_api_level = 0
     try:
         self.client.initialize()
         # Logged on
         self.omnik_api_level = 1
     except requests.exceptions.RequestException as err:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"Request error during account validation omnik portal: {err}")
     except requests.exceptions.HTTPError as errh:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"HTTP error during account validation omnik portal: {errh}")
     except requests.exceptions.ConnectionError as errc:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"Connection error during account validation omnik portal: {errc}"
         )
     except requests.exceptions.Timeout as errt:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"Timeout error during account validation omnik portal: {errt}"
         )
     except Exception as e:
         hybridlogger.ha_log(self.logger, self.hass_api, "ERROR", e)
 def _fetch_plants(self):
     # Fetch te plant's available for the account
     if (not self.plant_update or self.omnik_api_level == 1):
         try:
             plants = self.client.getPlants()
             for pid in plants:
                 self.plant_update[str(
                     pid['plant_id'])] = self.last_update_time
             self.omnik_api_level = 2
         except requests.exceptions.RequestException as err:
             hybridlogger.ha_log(self.logger, self.hass_api, "WARNING",
                                 f"Request error: {err}")
             self.omnik_api_level = 0
             return False
         except requests.exceptions.HTTPError as errh:
             hybridlogger.ha_log(self.logger, self.hass_api, "WARNING",
                                 f"HTTP error: {errh}")
             self.omnik_api_level = 0
             return False
         except requests.exceptions.ConnectionError as errc:
             hybridlogger.ha_log(self.logger, self.hass_api, "WARNING",
                                 f"Connection error: {errc}")
             self.omnik_api_level = 0
             return False
         except requests.exceptions.Timeout as errt:
             hybridlogger.ha_log(self.logger, self.hass_api, "WARNING",
                                 f"Timeout error: {errt}")
             self.omnik_api_level = 0
             return False
         except Exception as e:
             hybridlogger.ha_log(self.logger, self.hass_api, "ERROR", e)
             self.omnik_api_level = 0
             return False
     return True
Exemple #8
0
    def getPlants(self):
        # Get station list
        url = f"{self.base_url}/station/v1.0/list"
        json = {}

        stationlist = self._api_request(url, json=json)
        if not stationlist or not stationlist.get("success"):
            hybridlogger.ha_log(
                self.logger,
                self.hass_api,
                "WARNING",
                "plant/station list cannot be loaded from the cloud config, no valid data available",
            )
            return None

        # Get INVERTER devices for retreived stations
        data = []
        for station in stationlist.get("stationList"):
            # Only append stations that have a valid INVERTER device
            url = f"{self.base_url}/station/v1.0/device"
            json = {"stationId": station.get("id")}
            devicelist = self._api_request(url, json=json)
            for device in devicelist.get("deviceListItems"):
                if device.get("deviceType") == "INVERTER":
                    data.append({
                        "plant_id":
                        f'{str(station.get("id"))},{str(device.get("deviceSn"))}',
                    })

        return data
 def terminate(self):
     hybridlogger.ha_log(logger, self, "INFO",
                         "Stopping Omnikdatalogger...")
     self.rt.stop()
     self.datalogger.terminate()
     hybridlogger.ha_log(logger, self, "INFO",
                         "Omnikdatalogger was stopped")
Exemple #10
0
    def get_weather(self):
        try:
            if 'weather' not in self.cache:
                self.logger.debug('[cache miss] Fetching weather data')
                url = "https://{endpoint}/data/2.5/weather?lon={lon}&lat={lat}&units={units}&APPID={api_key}".format(
                    endpoint=self.config.get(
                        'openweathermap',
                        'endpoint',
                        fallback='api.openweathermap.org'),
                    lat=self.config.get('openweathermap', 'lat'),
                    lon=self.config.get('openweathermap', 'lon'),
                    units=self.config.get('openweathermap',
                                          'units',
                                          fallback='metric'),
                    api_key=self.config.get('openweathermap', 'api_key'),
                )

                res = requests.get(url)

                res.raise_for_status()

                self.cache['weather'] = res.json()

            return self.cache['weather']

        except requests.exceptions.HTTPError as e:
            hybridlogger.ha_log(
                self.logger, self.hass_api, "ERROR",
                'Unable to get weather data. [{0}]: {1}'.format(
                    type(e).__name__, str(e)))
            raise e
Exemple #11
0
    def getPlants(self):
        # Get the plant neeeded data from config
        data = []
        inverterdata = {}
        for plant in self.plant_id_list:
            inverter_sn = self.config.get(f"plant.{plant}",
                                          "inverter_sn",
                                          fallback=None)
            if not inverter_sn:
                hybridlogger.ha_log(
                    self.logger,
                    self.hass_api,
                    "ERROR",
                    "inverter_sn (The serial number of the inverter) for "
                    f"plant {plant} was not specified for [TCPclient]",
                )
                raise Exception(
                    "inverter_sn (a serial number of the inverter) was not specified"
                )
            inverterdata = {"inverter_sn": inverter_sn}
            self.inverters[plant] = inverterdata
            data.append({"plant_id": plant})

        hybridlogger.ha_log(self.logger, self.hass_api, "DEBUG",
                            f"plant list from config {data}")

        # The config was read, start listening
        # Claim the semaphore
        self.semaphore.acquire()
        for plugin in LocalProxyPlugin.localproxy_plugins:
            plugin.listen()
        # Release the semaphore
        self.semaphore.release()
        return data
 def _get_dsmr_data(self, plant, data):
     # if dsmr measurements are not enabled then return
     if not self.dsmr:
         return
     # Try to merge with the latest dsmr data available
     complete = False
     tries = 20
     while tries > 0:
         self.dsmr_access.acquire()
         if plant in self.dsmr_data:
             # Insert raw DSMR data
             data.update(self._dsmr_cache(plant, data['last_update']))
             # Insert calculated netto values (solar - net)
             self._calculate_consumption(data)
             complete = True
         self.dsmr_access.release()
         tries -= 1
         if not complete:
             time.sleep(1)
         else:
             break
     if not tries:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"Could not combine DSMR data for plant {plant}. "
             "Did you configure plant_id at your dsmr terminal config?")
Exemple #13
0
 def __init__(self, config, logger, hass_api, terminal_name,
              dsmr_serial_callback, dsmr_version):
     self.config = config
     self.logger = logger
     self.hass_api = hass_api
     self.terminal_name = terminal_name
     self.dsmr_serial_callback = dsmr_serial_callback
     self.mode = self.config.get(f"dsmr.{self.terminal_name}", 'mode',
                                 'device')
     if self.mode not in ['tcp', 'device']:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "ERROR",
             f"DSMR terminal {self.terminal_name} mode {self.mode} is not valid. "
             "Should be tcp or device. Ignoring DSMR configuration!")
         return
     self.device = self.config.get(f"dsmr.{self.terminal_name}", 'device',
                                   '/dev/ttyUSB0')
     self.host = self.config.get(f"dsmr.{self.terminal_name}", 'host',
                                 'localhost')
     self.port = self.config.get(f"dsmr.{self.terminal_name}", 'port',
                                 '3333')
     self.dsmr_version = dsmr_version
     # start terminal
     self.stop = False
     hybridlogger.ha_log(
         self.logger, self.hass_api, "INFO",
         f"Initializing DSMR termimal '{terminal_name}'. Mode: {self.mode}."
     )
     if self.mode == 'tcp':
         self.thr = threading.Thread(target=self._run_tcp_terminal,
                                     name=self.terminal_name)
     elif self.mode == 'device':
         self.thr = threading.Thread(target=self._run_serial_terminal,
                                     name=self.terminal_name)
     self.thr.start()
Exemple #14
0
 def getlist(self, section, option, fallback=[], **kwargs):
     if str(section).lower() == "default":
         if option in self.ha_args:
             payload = self.ha_args.get(option, fallback)
             if isinstance(payload, list):
                 return payload
             else:
                 hybridlogger.ha_log(
                     logger,
                     self,
                     "ERROR",
                     f"Config type error: Section: '{section}', Attribute: '{option}'; Expected <class 'list'> got {str(type(payload))}",
                 )
                 return fallback
     else:
         if str(section) in self.ha_args:
             if option in self.ha_args[section]:
                 payload = self.ha_args[section].get(option, fallback)
                 if isinstance(payload, list):
                     return payload
                 else:
                     hybridlogger.ha_log(
                         logger,
                         self,
                         "ERROR",
                         f"Config type error: Section: '{section}'; Attribute: '{option}'; Expected <class 'list'> got {str(type(payload))}",
                     )
                     return fallback
     try:
         retval = super().get(section, option, fallback=fallback, **kwargs)
     except Exception:
         retval = fallback
         pass
     return retval
    def getPlants(self):
        url = f'{self.base_url}/plant/list'

        data = self._api_request(url, 'GET', None)
        hybridlogger.ha_log(self.logger, self.hass_api, "DEBUG",
                            f"plant list {data}")

        return data['data'].get('plants', [])
Exemple #16
0
 def __init__(self):
     super().__init__()
     hybridlogger.ha_log(self.logger, self.hass_api, "INFO", "localproxy client plugin: HASSAPI")
     self.hass_handle = None
     if not self.hass_api:
         hybridlogger.ha_log(self.logger, self.hass_api,
                             "ERROR", "No HassAPI detected. Use AppDaemon with Home Assistent for this plugin")
         return
     self.logger_entity = self.config.get('client.localproxy.hassapi', 'logger_entity', 'binary_sensor.datalogger')
Exemple #17
0
 def log_available_fields(self, msg={}):
     """Logs the available output fields to the debug log. This helps to determine waht fields are available."""
     # Log available fields
     hybridlogger.ha_log(
         self.logger,
         self.hass_api,
         "DEBUG",
         f"Output for '{self.name}'. Fields: {list(msg.keys())}",
     )
 def _output_update(self, plant, data):
     # Insert dummy data for fields that have not been supplied by the client
     data = self._validate_client_data(plant, data)
     # Process for each plugin, but only when valid
     for plugin in Plugin.plugins:
         if not plugin.process_aggregates or 'sys_id' in data:
             hybridlogger.ha_log(
                 self.logger, self.hass_api, "DEBUG",
                 f"Trigger plugin '{getattr(plugin, 'name')}'.")
             plugin.process(msg=data)
    def getPlants(self):

        data = []
        for plant in self.config.getlist('client.solarmanpv', 'plant_id_list'):
            data.append({'plant_id': plant})

        hybridlogger.ha_log(self.logger, self.hass_api, "DEBUG",
                            f"plant list from config {data}")

        return data
Exemple #20
0
 def terminate(self):
     try:
         # Shutting down tcp server
         self.tcpServer.shutdown()
     except Exception as e:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "WARNING",
             f"Error shutting down tcp_proxy server. Error: {e}.")
         # exit the hard way!
         os.sys.exit(1)
Exemple #21
0
    def _init_terminals(self, terminals):
        self.terminals = {}
        self.sync = {}
        self.cache = {}
        self.ts_last_telegram = {}
        self.last_gas_update = {}
        self.tconfig = {}
        self.tconfig['tarif'] = {}
        if self.config.has_option('dsmr', 'tarif'):
            tarif_list = self.config.getlist('dsmr', 'tarif')
        else:
            tarif_list = ['0001', '0002']
            self.tconfig['tarif']['0001'] = 'low'
            self.tconfig['tarif']['0002'] = 'normal'
        for tarif in tarif_list:
            if tarif in self.tconfig['tarif']:
                default = self.tconfig['tarif'][tarif]
            else:
                default = tarif
            self.tconfig['tarif'][tarif] = self.config.get(
                'dsmr', f"tarif.{tarif}", default)
        for terminal in terminals:
            # Pre read configuration for each terminal
            self.tconfig[terminal] = {}

            self.tconfig[terminal]['plant_id'] = self.config.get(
                f"dsmr.{terminal}", 'plant_id', '')
            self.tconfig[terminal]['gas_meter'] = self.config.getboolean(
                f"dsmr.{terminal}", 'gas_meter', True)
            self.tconfig[terminal]['dsmr_version'] = self.config.get(
                f"dsmr.{terminal}", 'dsmr_version ', '5')
            self.tconfig[terminal]['total_energy_offset'] = \
                Decimal(self.config.get(f"dsmr.{terminal}", 'total_energy_offset', '0'))

            # Init terminal sync parameters
            self.sync[terminal] = 0
            self.cache[terminal] = Decimal(1000000)
            self.ts_last_telegram[terminal] = 0
            self.last_gas_update[terminal] = [
                0, Decimal('0.0'), Decimal('0.000')
            ]

            # Warnings and errors
            if not self.tconfig[terminal]['plant_id']:
                hybridlogger.ha_log(
                    self.logger, self.hass_api, "WARNING",
                    f"DSMR 'plant_id' for terminal '{terminal}' is not specified. "
                    "Received data will be NOT be associated with your solar data "
                    "and will be processed as stand-a-lone data!")
            # Initialize terminal
            self.terminals[terminal] = Terminal(
                self.config, self.logger, self.hass_api, terminal,
                self.dsmr_serial_callback,
                self.tconfig[terminal]['dsmr_version'])
Exemple #22
0
 def _run(self):
     # TCP listen loop
     try:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "INFO",
             f"Starting tcp_proxy server. listening at {self.listenaddress[0]}:{self.listenaddress[1]}."
         )
         self.tcpServer.serve_forever()
     except Exception as e:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "ERROR",
             f"Error binding to {self.listenaddress}. Error: {e}.")
Exemple #23
0
 def listen(self):
     if self.hass_api:
         for logger_entity in self.logger_entity:
             self.hass_handle[logger_entity] = self.hass_api.listen_state(
                 self._run, logger_entity, attribute="data"
             )
             hybridlogger.ha_log(
                 self.logger,
                 self.hass_api,
                 "INFO",
                 f"HASSapi listening to. '{logger_entity}', attribute: 'data'",
             )
 def _sunshine_check(self):
     self.sundown = not self.dl.sun_shine()
     if self.sundown:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "INFO",
             f"No sunshine postponing till down next dawn {self.dl.next_dawn}."
         )
         # Send 0 Watt update
         return self.dl.next_dawn + timedelta(minutes=10)
     else:
         # return the last report time return value, but not when there is no sun
         return self.last_update_time
 def _update_persistant_cache(self):
     try:
         file_cache = {}
         for item in self.cache:
             file_cache[item] = float(self.cache[item])
         with open(self.persistant_cache_file, 'w') as json_file_config:
             json.dump(file_cache, json_file_config)
     except Exception as e:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "ERROR",
             f"Cache file '{self.persistant_cache_file}' can not be written! Error: {e.args}"
         )
Exemple #26
0
 def _process_gas(self, msg_dsmr, telegram):
     terminal = threading.currentThread().getName()
     if not self.tconfig[terminal]["gas_meter"]:
         # Skip gas meter
         return
     try:
         if self.tconfig[terminal]["dsmr_version"] in ["4", "5"]:
             G = telegram[obis_references.HOURLY_GAS_METER_READING]
             msg_dsmr["gas_consumption_total"] = G.values[1]["value"]
             msg_dsmr["timestamp_gas"] = datetime.timestamp(G.values[0]["value"])
         elif self.tconfig[terminal]["dsmr_version"] == "5B":
             G = telegram[obis_references.BELGIUM_HOURLY_GAS_METER_READING]
             msg_dsmr["gas_consumption_total"] = G.values[1]["value"]
             msg_dsmr["timestamp_gas"] = datetime.timestamp(G.values[0]["value"])
         elif self.tconfig[terminal]["dsmr_version"] == "2.2":
             G = telegram[obis_references.GAS_METER_READING]
             msg_dsmr["gas_consumption_total"] = G.values[6]["value"]
             msg_dsmr["timestamp_gas"] = datetime.timestamp(G.values[0]["value"])
         msg_dsmr["EQUIPMENT_IDENTIFIER_GAS"] = telegram[
             obis_references.EQUIPMENT_IDENTIFIER_GAS
         ].value
         # Set last value to the cache
         if not self.last_gas_update[terminal][0]:
             self.last_gas_update[terminal][0] = msg_dsmr["timestamp_gas"]
             self.last_gas_update[terminal][1] = msg_dsmr["gas_consumption_total"]
         # Calculate gas consumption / hour self.last_gas_update[terminal] = [0, Decimal('0')]
         if msg_dsmr["timestamp_gas"] > self.last_gas_update[terminal][0]:
             msg_dsmr["gas_consumption_hour"] = (
                 (
                     msg_dsmr["gas_consumption_total"]
                     - self.last_gas_update[terminal][1]
                 )
                 * Decimal("3600")
                 / Decimal(
                     msg_dsmr["timestamp_gas"] - self.last_gas_update[terminal][0]
                 )
             ).quantize(Decimal("0.001"))
             self.last_gas_update[terminal][0] = msg_dsmr["timestamp_gas"]
             self.last_gas_update[terminal][1] = msg_dsmr["gas_consumption_total"]
             self.last_gas_update[terminal][2] = msg_dsmr["gas_consumption_hour"]
         else:
             msg_dsmr["gas_consumption_hour"] = self.last_gas_update[terminal][2]
     except Exception as e:
         hybridlogger.ha_log(
             self.logger,
             self.hass_api,
             "WARNING",
             f"DSMR gas_meter reading for terminal '{terminal}' failed. "
             f"Check your config: Error: {e.args}",
         )
 def _load_persistant_cache(self):
     try:
         with open(self.persistant_cache_file) as total_energy_cache:
             file_cache = json.load(total_energy_cache)
             for item in file_cache:
                 self.cache[item] = Decimal(f"{file_cache[item]}")
         hybridlogger.ha_log(
             self.logger, self.hass_api, "INFO",
             f"Using energy cache file '{self.persistant_cache_file}'.")
     except Exception as e:
         hybridlogger.ha_log(
             self.logger, self.hass_api, "INFO",
             f"Cache file '{self.persistant_cache_file}' was not used previously. Error: {e.args}"
         )
    def initialize(self):
        pwhash = hashlib.md5(self.password.encode('utf-8')).hexdigest()
        url = f'{self.base_url}/Login?username={self.username}&password={pwhash}&key={self.api_key}'

        xmldata = self._api_request(url, 'GET', None)
        hybridlogger.ha_log(self.logger, self.hass_api, "DEBUG",
                            f"account validation: {xmldata}")

        # Parsing the XML
        for element in ET.fromstring(xmldata):
            if element.tag == "userID":
                self.user_id = int(element.text)
            elif element.tag == "token":
                self.token = element.text
Exemple #29
0
 def _run(self):
     self.is_running = False
     # The function calls DataLogger.process()
     self.last_update_time = self.datalogger.process(
         *self.args, **self.kwargs)
     # Calculate the new timer interval
     if self.last_update_time:
         # Reset retry counter
         self.retries = 0
         if self.last_update_time <= datetime.now(timezone.utc):
             # If last report time + 2x interval is less than the current time then increase
             self.new_report_expected_at = self.last_update_time + timedelta(
                 seconds=self.interval)
             # Check if we have at least 60 seconds for the next cycle
             if self.new_report_expected_at + timedelta(
                     seconds=-10) < datetime.now(timezone.utc):
                 # No recent update or missing update: wait {interval}/5 from now()
                 self.new_report_expected_at = datetime.now(
                     timezone.utc) + timedelta(seconds=self.interval / 5)
         else:
             # Skipping dark period
             self.new_report_expected_at = self.last_update_time
         self.calculated_interval = (self.new_report_expected_at -
                                     datetime.now(timezone.utc)).seconds
     else:
         # An error occured calculate retry interval
         retry_interval = self.half_interval
         i = self.retries
         while i > 0:
             # Double retry interval to avoid to much traffic
             retry_interval *= 2
             i -= 1
         # Increment retry counter maximal interval between retries is half_interval * 2 * 2 * 2 = 4 intervals
         if self.retries < 3:
             self.retries += 1
         # Calculate new report time
         self.new_report_expected_at = datetime.now(
             timezone.utc) + timedelta(seconds=retry_interval)
         self.calculated_interval = (self.new_report_expected_at -
                                     datetime.now(timezone.utc)).seconds
     # Make sure we have at least 15 seconds on the time to prevent a deadlocked timer loop
     if self.calculated_interval < 15:
         self.calculated_interval = 15
     hybridlogger.ha_log(
         self.logger,
         self.hass_api,
         "DEBUG",
         f"new poll in {self.calculated_interval} seconds at {self.new_report_expected_at.isoformat()}.",
     )
     self.start()
Exemple #30
0
    def getPlants(self):
        data = []
        inverterdata = {}
        for plant in self.plant_id_list:
            inverter_address = self.config.get(f"plant.{plant}",
                                               "inverter_address",
                                               fallback=None)
            if not inverter_address:
                hybridlogger.ha_log(
                    self.logger,
                    self.hass_api,
                    "ERROR",
                    f"inverter_address for plant {plant} was not specified [TCPclient]",
                )
                raise Exception("an inverter_address was not specified")
            inverter_port = int(
                self.config.get(f"plant.{plant}",
                                "inverter_port",
                                fallback="8899"))
            inverter_connection = (inverter_address, int(inverter_port))
            logger_sn = self.config.get(f"plant.{plant}",
                                        "logger_sn",
                                        fallback=None)
            http_only = bool(
                self.config.get(f"plant.{plant}", "http_only", fallback=False))
            if not logger_sn:
                hybridlogger.ha_log(
                    self.logger,
                    self.hass_api,
                    "ERROR",
                    "logger_sn (The serial number of the "
                    "Wi-Fi datalogger) for plant {plant} was not specified for [TCPclient], http mode only",
                )
            inverter_sn = self.config.get(f"plant.{plant}",
                                          "inverter_sn",
                                          fallback=None)
            if not inverter_sn:
                hybridlogger.ha_log(
                    self.logger,
                    self.hass_api,
                    "WARNING",
                    "inverter_sn (The serial number of the inverter) "
                    "for plant {plant} was not specified for [TCPclient], http mode only",
                )
            inverterdata = {
                "inverter_address": inverter_address,
                "inverter_port": inverter_port,
                "logger_sn": int(logger_sn),
                "inverter_sn": inverter_sn,
                "inverter_connection": inverter_connection,
                "http_only": http_only,
            }
            self.inverters[plant] = inverterdata
            self._mode[plant] = 0
            data.append({"plant_id": plant})

        hybridlogger.ha_log(self.logger, self.hass_api, "DEBUG",
                            f"plant list from config {data}")

        return data