Ankündigung

Einklappen
Keine Ankündigung bisher.

Simulation plugin

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    #61
    Guten Abend psilo,

    leider hilft mir die 1.5.0.7 aus dem DEV auch nicht, ich versuche nun mangels Fern-Debugger mithilfe von eingefügten debug-log-Einträgen die Ursache heraus zu finden... die update-Routine wird zumindest schonmal korrekt aufgerufen, aber der Sprung in die erste if-Abfrage scheitert bereits

    komischweise ist im webif korrekt die Version 1.5.0.7 angezeigt aber die zusätzlichen Infos zu den Control-items wie aus deinem Screenshot fehlen mir
    Zuletzt geändert von fhartlieb; 23.11.2018, 21:34.

    Kommentar


      #62
      ich hab es heraus gefunden...
      Wenn ich die eigentlich optionalen callers in plugin.yaml wieder entfernt habe, dann funktioniert es. Dies wollte ich jedoch nicht so akzeptieren und habe weiter geforscht:

      mit folgendem Code in __init__.py simulation
      Code:
      ...
          def update_item(self, item, caller=None, source=None, dest=None):
              self.logger.debug('Update item: {}'.format(item))
              self.logger.debug('caller: {}'.format(caller))
              self.logger.debug('callers: {}'.format(self._callers))
              if (item.conf['sim'] == 'track') and (self.state() == 2) and (self._callers is None or caller in self._callers):
                  self.logger.debug('Update 1')
                  now = self.shtime.now()
      ...
      ergeben sich folgende log-Einträge:
      Code:
      2018-11-23  21:54:40 DEBUG    plugins.simulation Update item: Wohnzimmer Tischlampe
      2018-11-23  21:54:40 DEBUG    plugins.simulation caller: Visu
      2018-11-23  21:54:40 DEBUG    plugins.simulation callers: ['knx', 'visu']
      tja, so steht es ja auch in der Beschreibung... wer lesen kann ist klar im Vorteil: "Be aware that the caller in the list has to be case sensitive. Otherwise it won’t trigger."
      dies habe ich leider nicht befolgt

      entweder Groß-&Kleinschreibung bei den callern beachten oder mit dem simulation plugin 1.5.0.7 aus dem DEV-git keine caller in der plugin.yaml angeben:
      Code:
      simulation:
          class_name: Simulation
          class_path: plugins.simulation
          data_file: /usr/local/smarthome/var/db/simulation.txt
          # callers:
              # - KNX
              # - Visu
      btw: im dev-git 1.5.0.7 steht noch bei der logger-Info die Version 1.5.0.6 in Zeile 55 in der __init__.py
      Danke für den support psilo
      Zuletzt geändert von fhartlieb; 23.11.2018, 22:35.

      Kommentar


        #63
        danke für die info mit der falsch geloggten version. das hatte ich übersehen, da das plugin wie gesagt nicht initial von mir war ich referenziere auf die als attribut gesetzte version

        Kommentar


          #64
          Hallo zusammen,
          ich habe deine Frage zum Simulationsplugin. Mir ist aufgefallen (zumindest habe ich das gedacht), dass bei mir das Simulationsplugin irgendwann immer aufhört abzuspielen. Nachdem ich nun die DEBUG Logausgaben hochgesetzt habe, habe ich vermutlich die Ursache ermittelt. Aber ich verstehe nicht warum das so gelöst ist, bzw. ob das so sein soll.

          In meinem simulation.txt File steht folgendes
          Code:
          Mo;11:27:23;R11.LICHT.DECKE;True;KNX
          Mo;11:27:25;R12.LICHT.HAUSTUER;True;KNX
          Mo;11:31:07;R11.LICHT.DECKE;False;KNX
          Mo;11:31:23;R12.LICHT.HAUSTUER;False;KNX
          Do;11:48:00;R02.LICHT.DECKE;True;KNX
          Do;11:50:45;R02.LICHT.DECKE;False;KNX
          Do;12:13:26;R24.LICHT.BETT.DIMMER;100.0;KNX
          Nun habe ich es gestartet um 11:16:21 und es läuft auch einwandfrei, bis "Mo;11:31:23;R12.LICHT.HAUSTUER;False;KNX". Danach scheint es zu stocken. Aber wenn ich die Logausgaben richtig deute, dann würde das Plugin morgen weitermachen.
          Code:
          2018-12-28  11:13:28 INFO     plugins.simulation   Init Simulation release 1.5.0.6
          2018-12-28  11:13:28 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.midnight
          2018-12-28  11:13:30 INFO     plugins.simulation   Starting Simulation
          2018-12-28  11:13:30 DEBUG    plugins.simulation   Tank: 14
          2018-12-28  11:13:30 DEBUG    plugins.simulation   last entry: 2018-12-28 09:18:41+01:00
          2018-12-28  11:13:30 DEBUG    plugins.simulation   now: 2018-12-28 11:13:30.179978+01:00
          2018-12-28  11:13:30 DEBUG    plugins.simulation   scheduler_remove: name = plugins.simulation.startrecord
          2018-12-28  11:13:30 DEBUG    plugins.simulation   Scheduling record start 2018-12-29 09:18:41+01:00
          2018-12-28  11:13:30 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.startrecord
          2018-12-28  11:16:19 DEBUG    plugins.simulation   scheduler_remove: name = plugins.simulation.startrecord
          2018-12-28  11:16:19 DEBUG    plugins.simulation   stop record
          2018-12-28  11:16:21 DEBUG    plugins.simulation   Starting playback
          2018-12-28  11:16:21 DEBUG    plugins.simulation   Scheduling R02.LICHT.DECKE False 2018-12-28 11:24:34.081232+01:00
          2018-12-28  11:16:21 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:24:34 DEBUG    plugins.simulation   Setting R02.LICHT.DECKE to False
          2018-12-28  11:24:34 DEBUG    plugins.simulation   Scheduling R04.LICHT.LINKS False 2018-12-28 11:26:47.135409+01:00
          2018-12-28  11:24:34 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:26:47 DEBUG    plugins.simulation   Setting R04.LICHT.LINKS to False
          2018-12-28  11:26:47 DEBUG    plugins.simulation   Scheduling R04.LICHT.RECHTS False 2018-12-28 11:26:48.433927+01:00
          2018-12-28  11:26:47 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:26:48 DEBUG    plugins.simulation   Setting R04.LICHT.RECHTS to False
          2018-12-28  11:26:48 DEBUG    plugins.simulation   Scheduling R01.LICHT.DECKE False 2018-12-28 11:26:55.435092+01:00
          2018-12-28  11:26:48 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:26:55 DEBUG    plugins.simulation   Setting R01.LICHT.DECKE to False
          2018-12-28  11:26:55 DEBUG    plugins.simulation   Scheduling R01.LICHT.TREPPE False 2018-12-28 11:26:57.445675+01:00
          2018-12-28  11:26:55 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:26:57 DEBUG    plugins.simulation   Setting R01.LICHT.TREPPE to False
          2018-12-28  11:26:57 DEBUG    plugins.simulation   Scheduling R11.LICHT.DECKE True 2018-12-28 11:27:23.449685+01:00
          2018-12-28  11:26:57 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:27:23 DEBUG    plugins.simulation   Setting R11.LICHT.DECKE to True
          2018-12-28  11:27:23 DEBUG    plugins.simulation   Scheduling R12.LICHT.HAUSTUER True 2018-12-28 11:27:25.538558+01:00
          2018-12-28  11:27:23 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:27:26 DEBUG    plugins.simulation   Setting R12.LICHT.HAUSTUER to True
          2018-12-28  11:27:26 DEBUG    plugins.simulation   Scheduling R11.LICHT.DECKE False 2018-12-28 11:31:07.042995+01:00
          2018-12-28  11:27:26 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:31:07 DEBUG    plugins.simulation   Setting R11.LICHT.DECKE to False
          2018-12-28  11:31:07 DEBUG    plugins.simulation   Scheduling R12.LICHT.HAUSTUER False 2018-12-28 11:31:23.070037+01:00
          2018-12-28  11:31:07 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          2018-12-28  11:31:23 DEBUG    plugins.simulation   Setting R12.LICHT.HAUSTUER to False
          2018-12-28  11:31:23 DEBUG    plugins.simulation   [MARKIEREN]Found next day[/MARKIEREN] R02.LICHT.DECKE True 2018-12-28 11:48:00.096499+01:00 shitfing to tomorrow.
          2018-12-28  11:31:23 DEBUG    plugins.simulation   Scheduling R02.LICHT.DECKE True 2018-12-[MARKIEREN]29[/MARKIEREN] 11:48:00.096499+01:00
          2018-12-28  11:31:23 DEBUG    plugins.simulation   scheduler_add: name = plugins.simulation.simulate
          Warum ist das so? Ja es ist richtig, dass hier ein anderer Tag aufgenommen wurde. Hatte es immer so verstanden, dass das Plugin so arbeitet um volle Tage aufzunehmen, wenn es öfter unterbrochen wird. Ich hätte erwartet, dass es hier einfach weitergelaufen wäre beim Abspielen und das Thema mit dem "Next Day" wirklich erst bei dem Tag passiert. Dieses kommt aber deutlich später in der Datei.

          Code:
          Mo;11:31:07;R11.LICHT.DECKE;False;KNX
          Mo;11:31:23;R12.LICHT.HAUSTUER;False;KNX
          Do;11:48:00;R02.LICHT.DECKE;True;KNX
          Do;11:50:45;R02.LICHT.DECKE;False;KNX
          Do;12:13:26;R24.LICHT.BETT.DIMMER;100.0;KNX
          ....
          Do;23:29:41;R23.LICHT.DECKE;True;KNX
          Do;23:42:06;R24.LICHT.DECKE;True;KNX
          Do;23:50:13;R01.LICHT.DECKE;True;KNX
          Do;23:50:20;R02.LICHT.DECKE;True;KNX
          NextDay
          Fr;00:09:33;R11.LICHT.DECKE;True;KNX
          Fr;00:09:35;R02.LICHT.DECKE;False;KNX
          Fr;00:09:35;R11.LICHT.DECKE;False;KNX
          Fr;00:09:35;R01.LICHT.DECKE;False;KNX
          So bedeutet das doch, dass bei einem Tagwechsel bei der Aufzeichnung immer das Abspielen unterbrochen wird.

          Kann man das irgendwo einstellen? Mache ich einen Gedankenfehler?

          SmartVISU2.9dev
          Simulation Plugin 1.5.0.6

          Danke euch

          Gruß
          loeserman

          Kommentar


            #65
            Moin zusammen,
            ich habe bei mir mal das Plugin angepasst. So läuft es aus meiner Sicht korrekt.

            plugins\simulation\__init__.py
            Code:
            [COLOR=#008000]    # --------------------------------- _set_item -----------------------------------
                # Is called by the scheduler. Sets the item, reads the next event and
                # schedules it.[/COLOR]
            
                def _set_item(self, **kwargs):
                    target = None
                    value = None
            [COLOR=#FF0000]        nextday = False                                                                                                      # v0.7L Anpassung wegen Nextday[/COLOR]
                    if 'target' in kwargs:
                        target = kwargs['target']
                    if 'value' in kwargs:
                        value = kwargs['value']
                    if target is not None and value is not None:
                        self.logger.debug('Setting {} to {}'.format(target, value))
                        item = self.items.return_item(target)
                        try:
                            item(value, caller='Simulation')
                        except:
                            self.logger.error('Skipped unknown item: {}'.format(target))
                    entry = self.file.readline()
                    if entry == 'NextDay\n':
                        entry = self.file.readline()
            [COLOR=#FF0000]            nextday = True                                                                                                   # v0.7L Anpassung wegen Nextday[/COLOR]
                    if entry != '':
                        day = entry.split(';')[0]
                        time = entry.split(';')[1]
                        target = entry.split(';')[2]
                        value = entry.split(';')[3]
                        hour = int(time.split(':')[0])
                        minute = int(time.split(':')[1])
                        seconds = int(time.split(':')[2])
                        now = self.shtime.now()
                        next = now.replace(hour=hour, minute=minute, second=seconds)
                        dif = next - now
            [S][COLOR=#FF0000]            if (self.lastday != '') and (self.lastday != day):[/COLOR][/S]
            [COLOR=#FF0000]            if (nextday == True):                                                                                            # v0.7L Anpassung wegen Nextday[/COLOR]
                            self.logger.debug('Found next day {} {} {} shitfing to tomorrow.'.format(target, value, next))
                            next = next + timedelta(1)
                        self._message_item('Next event: {}<br>{}   {}'.format(time, target, value, 'Simulation'))
                        self.logger.debug('Scheduling {} {} {}'.format(target, value, next))
                        self.scheduler_add('simulate', self._set_item, value={'target': target, 'value': value}, next=next)
                        self.lastday = day
                    else:
                        self.logger.info('End of file reached, simulation ended')
                        self._message_item('Simulation ended', 'Simulation')
                        self.state(0, 'Simulation')
            Vielleicht hilft es ja auch anderen.
            Gucke mal, ob ich das noch im github irgendwie eintragen kann, so dass der Entwickler da nochmal drübergucken kann.

            Gruß
            loeserman

            Kommentar


              #66
              Erklärt bitte nochmal etwas genauer, bevor ich mich da zu sehr reindenken muss: euer Problem ist, dass das Plugin wenn es bspw. Mo und Do (aber nicht Di und Mi) aufgezeichnet hat, beim Abspielen den Donnerstag am Dienstag spielt?

              Der Autor hat leider schon länger keine Wartung mehr gemacht, die letzten Anpassungen und das WebIf stammen von mir. Da ich mich gefühlt aber um 2 dutzend Plugins kümmern muss, möchte ich möglichst wenig Arbeit investieren, um die Anpassung zu pushen..

              Kommentar


                #67
                Hallo zusammen,
                ein frohes neues Jahr und Danke psilo, dass Du Dich um das Plugin kümmerst, obwohl Du nicht der Autor bist. Echt klasse.

                Also, so wie ich das vestanden habe arbeitet das Plugin beim Aufzeichnen folgendermaßen:

                Wenn es im Zustand Record ist, dann nimmt es alle Item Änderungen auf von Items, die mit einem entsprechenden Tag markiert sind. Diese Änderungen werden dann mit dem Tag (Mo..So) und dem Zeitstempel in eine Datei geschrieben.

                Beispiel:
                Fr;10:06:37;R02.LICHT.DECKE;True;KNX
                Fr;10:07:43;R02.LICHT.DECKE;False;KNX
                ...

                Läuft die Aufzeichnung über einen Tag hinweg, dann wird in der Aufzeichnung ein Schlüsselwort "NextDay" eingefügt und danach so wie oben weitergeschrieben.

                Beispiel:
                So;22:14:38;R23.LICHT.BETT_RECHTS.DIMMER;0.0;KNX
                NextDay
                Mo;06:07:28;R23.LICHT.BETT_LINKS.DIMMER;9.8;KNX
                Mo;06:07:28;R23.LICHT.BETT_LINKS.DIMMER;19.6;KNX

                Soweit so gut. Nun kann aber die Aufzeichnung jederzeit unterbrochen werden, weil man zum Beispiel am obigen Freitag um 11 Uhr das Haus verlässt. Das Plugin zeichnet dann auch nichts mehr auf. So soll es sein. Wenn man dann am Freitag um 18 Uhr wieder nach Hause kommt und das Plugin wieder aktiv schaltet, dann prüft es den letzten Eintrag (Fr;10:07:43;R02.LICHT.DECKE;False;KNX) und stellt fest, dass die Differenz von 18 Uhr zu 10:07 Uhr zu groß ist. Es macht daher mehr Sinn einen Tag zu warten und ab Samstag 10:07:43 weiter aufzuzeichnen, um einen ganzen Tag zu erfassen. Das macht das Plugin auch tadellos und geht in Pause. Am Samstag schaltet es dann eigenständig um 10:07:43 wieder in den Zustand Record. Daher ergeben sich dann die folgenden Einträge:

                Fr;10:06:37;R02.LICHT.DECKE;True;KNX
                Fr;10:07:43;R02.LICHT.DECKE;False;KNX
                Sa;10:11:10;R12.LICHT.GARDEROBE;True;KNX
                Sa;10:11:11;R12.LICHT.GARDEROBE;False;KNX
                ...

                Das macht das Plugin so lange, bis während einer aktiven Aufzeichnung tatsächlich über 0 Uhr gesprungen wird, dann gibt es den Eintrag "NextDay"

                Fr;10:06:37;R02.LICHT.DECKE;True;KNX
                Fr;10:07:43;R02.LICHT.DECKE;False;KNX
                Sa;10:11:10;R12.LICHT.GARDEROBE;True;KNX
                Sa;10:11:11;R12.LICHT.GARDEROBE;False;KNX
                ...
                So;22:14:38;R23.LICHT.BETT_RECHTS.DIMMER;0.0;KNX
                NextDay
                Mo;06:07:28;R23.LICHT.BETT_LINKS.DIMMER;9.8;KNX
                Mo;06:07:28;R23.LICHT.BETT_LINKS.DIMMER;19.6;KNX


                Nun kommen wir zum Problem aus meiner Sicht:

                Schaltet man das Plugin in Replay, dann geht es zum Aufang der Aufzeichnung, sucht den nächsten Zeitpunkt und spielt die Aufzeichnung ab. Das ist völlig unabhängig vom Wochentag, an dem aufgezeichnet wurde.

                Nehmen wir an, wir haben nun Mittwoch 10:07:00 Uhr und schalten das Plugin in Replay, dann wird es feststellen, dass der nächste Zeitpunkt 10:07:43 ist. Zu dieser Zeit führt es dann die Aktion R02.LICHT.DECKE;False aus. Aber dann würde ich erwarten, dass es dann um 10:11:10 die Aktion R12.LICHT.GARDEROBE;True ausführt. Es gehört ja mit zur gleichen Tagscheibe die aufgezeichnet wurde.

                Das passiert aber nicht. Bei dem Replay guckt das Plugin auch nach, ob sich der Tag geändert hat bei der Aufzeichnung. Ist das der Fall, dann wird der Scheduler um einen Tag erhöht. Das bedeutet dann, dass die Aktion R12.LICHT.GARDEROBE;True nicht mehr am Mittwoch ausgeführt wird, sondern am Donnerstag. Am Mittwoch passiert dann einfach gar nichts mehr.

                Code:
                if (self.lastday != '') and (self.lastday != day):
                    self.logger.debug('Found next day {} {} {} shitfing to tomorrow.'.format(target, value, next))
                    next = next + timedelta(1)
                Diese Erhöhung des Schedulers auf den nächsten Tag darf es aber nur geben, wenn die Kennung "NextDay" auftritt. Dann beginnt die nächste aufgezeichnete Tagscheibe.

                Anbei hänge ich mal meinen aktuellen plugin Code. Damit kannst Du dann leichter einen DIFF machen, oder wie das heißt, wenn man im Git etwas vergleicht. Die wesentliche Änderung ist im Codeabschnitt "_set_item". Ist aber auch kommentiert.

                Code:
                # 1!/usr/bin/env python3
                # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
                #########################################################################
                # Copyright 2013 KNX-User-Forum e.V.            http://knx-user-forum.de/
                #########################################################################
                #
                #  SmartHomeNG is free software: you can redistribute it and/or modify
                #  it under the terms of the GNU General Public License as published by
                #  the Free Software Foundation, either version 3 of the License, or
                #  (at your option) any later version.
                #
                #  SmartHomeNG is distributed in the hope that it will be useful,
                #  but WITHOUT ANY WARRANTY; without even the implied warranty of
                #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                #  GNU General Public License for more details.
                #
                #  You should have received a copy of the GNU General Public License
                #  along with SmartHomeNG. If not, see <http://www.gnu.org/licenses/>.
                #
                #  ToDo
                #  Fehlermeldung wenn Datei nicht geschrieben werden kann
                #  Language Settings
                #  Day is out of range for Month...  
                #  Replay simulation from beginning when end of file is reached
                #
                #  Releases:
                #  0.1  initial
                #  0.2  fixed bug when play is pushed while recording
                #       removed rest of state 3 (hold)
                #  0.3  changed most logger.info to logger.debug
                #       Added release version to init message
                #  0.4  Changed logging style
                #       corrected serious bug in compare entry with NextDay
                #  0.5  Added feature to select which caller is written to the simulation file
                #  0.6  Added WebGUI and Clear Data File function
                #
                #  0.7L Fix: Nextday will be checked correctly during replay
                #
                ##########################################################################
                
                import logging
                from datetime import datetime, timedelta
                from lib.model.smartplugin import *
                from lib.shtime import Shtime
                from lib.module import Modules
                from lib.item import Items
                from lib.scheduler import Scheduler
                
                
                class Simulation(SmartPlugin):
                    ALLOW_MULTIINSTANCE = False
                    PLUGIN_VERSION = "1.5.0.7"                                                    # 0.7L Anpassung der Pluginversion, mit dem eigenen Fix für die NextDay anpassung.
                
                    def __init__(self, smarthome, data_file, callers=None):
                        self.logger = logging.getLogger(__name__)
                        self.logger.info('Init Simulation release 1.5.0.7 - Nextday Fix')         # 0.7L Anpassung des Logeintrags, mit dem eigenen Fix für die NextDay anpassung
                        self._sh = smarthome
                        self.shtime = Shtime.get_instance()
                        self._datafile = data_file
                        self.lastday = ''
                        self.items = Items.get_instance()
                        self.scheduler = Scheduler.get_instance()
                        self._callers = callers
                        self._items = []
                        self.scheduler_add('midnight', self._midnight, cron='0 0 * *', prio=3)
                
                        if not self.init_webinterface():
                            self._init_complete = False
                
                    def run(self):
                        self.logger.info('Starting Simulation')
                        self.alive = True
                        self._start_record()
                
                        # if you want to create child threads, do not make them daemon = True!
                        # They will not shutdown properly. (It's a python bug)
                
                    def stop(self):
                        self.logger.info('Exit Simulation')
                        try:
                            self.file.close()
                        except:
                            self.logger.error('No file to close')
                        self.alive = False
                
                    # --------------------------------- parse_item ----------------------------------
                    def parse_item(self, item):
                        if 'sim' in item.conf:
                            if item.conf['sim'] == 'message':
                                self._message_item = item
                            if item.conf['sim'] == 'state':
                                self.state = item
                            if item.conf['sim'] == 'control':
                                self.control = item
                            if item.conf['sim'] == 'tank':
                                self.tank = item
                            self._items.append(item)
                            return self.update_item
                        else:
                            return None
                
                    # --------------------------------- parse_logic ---------------------------------
                    def parse_logic(self, logic):
                        if 'xxx' in logic.conf:
                            # self.function(logic['name'])
                            pass
                
                    # --------------------------------- update_item ---------------------------------
                    # Callback. Writes the event to the file
                
                    def update_item(self, item, caller=None, source=None, dest=None):
                        if (item.conf['sim'] == 'track') and (self.state() == 2) and (self._callers and caller in self._callers):
                            now = self.shtime.now()
                            day = now.day
                            self.file.write(now.strftime('%a;%H:%M:%S'))
                            self.file.write(';')
                            self.file.write(item.id())
                            self.file.write(';')
                            self.file.write('{}'.format(item()))
                            self.file.write(';')
                            self.file.write(caller)
                            self.file.write('\n')
                            self.file.flush()
                            self._message_item(
                                'Last event recorded: {} - {}   {}'.format(now.strftime('%H:%M:%S'), item.id(), item(), 'Simulation'))                         # 0.7L Textausgabe ohne <br>, da dies in der Visu nicht als HTML sondern als Text angezeigt wird
                            return None
                        if (item.conf['sim'] == 'control') and (caller != 'Simulation'):
                            self.state_selector[self.state(), self.control()](self)
                            self.control(0, 'Simulation')
                
                    # ----------------------- _start_record ---------------------------
                    # Called by run() and by the state machine. Compares times
                    # and schedules recording accordingly.
                
                    def _start_record(self):
                        tank = self._get_tank()
                        self.logger.debug('Tank: {}'.format(tank))
                        self.tank(tank)
                        now = self.shtime.now()
                        last_entry = self._lastentry.replace(year=now.year, month=now.month, day=now.day, tzinfo=now.tzinfo)
                        self.logger.debug('last entry: {}'.format(last_entry))
                        self.logger.debug('now: {}'.format(now))
                        now -= timedelta(minutes=15)
                        if now.time() < last_entry.time():
                            self._schedule_recording_start(last_entry)
                        else:
                            start = last_entry
                            start += timedelta(days=1)
                            self._schedule_recording_start(start)
                
                    # ----------------------- _schedule_recording_start ---------------------------
                    def _schedule_recording_start(self, time):
                        self.state(1, 'Simulation')
                        self.scheduler_remove('startrecord')
                        self._message_item('Recording starts {}'.format(time), caller='Simulation')
                        self.logger.debug('Scheduling record start {}'.format(time))
                        self.scheduler_add('startrecord', self._start_recording, next=time)
                
                    # ----------------------------- _start_recording --------------------------------
                    def _start_recording(self):
                        self.state(2, 'Simulation')
                        self.scheduler_remove('startrecord')
                        self._message_item('Recording', caller='Simulation')
                        self.logger.debug('starting record')
                        self.recording = True
                        try:
                            self.file = open(self._datafile, 'a')
                        except IOError as error:
                            self.logger.error('Cannot open file {} for writing: {}'.format(self._datafile, error))
                            self._message_item('cannot write to file', 'Simulation')
                            self.state(0, 'Simulation')
                
                    # ----------------------------- _stop_recording ---------------------------------
                    def _stop_recording(self):
                        self.state(0, 'Simulation')
                        self.scheduler_remove('startrecord')
                        self._message_item('', caller='Simulation')
                        self.logger.debug('stop record')
                        try:
                            self.file.close()
                        except:
                            self.logger.error('Not running')
                
                    # ----------------------------- _start_playbacl ---------------------------------
                    def _start_playback(self):
                        self.state(4, 'Simulation')
                        self.logger.debug('Starting playback')
                        try:
                            self.file = open(self._datafile, 'r')
                            self._wind_until_now()
                            self._set_item()
                        except IOError as error:
                            self.logger.error('NoFile {}'.format(error))
                            self._message_item('No File', 'Simulation')
                            self.state(0, 'Simulation')
                
                    # ----------------------------- _stop_playback ---------------------------------
                    def _stop_playback(self):
                        self.state(0, 'Simulation')
                        self.logger.debug('Stopping playback')
                        self.scheduler_remove('simulate')
                        self._message_item('Playback stopped', 'Simulation')
                        try:
                            self.file.close()
                        except:
                            self.logger.error('No fileto close')
                
                    # --------------------------------- _set_item -----------------------------------
                    # Is called by the scheduler. Sets the item, reads the next event and
                    # schedules it.
                
                    def _set_item(self, **kwargs):
                        target = None
                        value = None
                        nextday = False                                                                                                      # 0.7L Anpassung wegen Nextday, Neue Variable eingeführt, die besagt True wird, wenn die NextDay Kennung erkannt wurde
                        if 'target' in kwargs:
                            target = kwargs['target']
                        if 'value' in kwargs:
                            value = kwargs['value']
                        if target is not None and value is not None:
                            self.logger.debug('Setting {} to {}'.format(target, value))
                            item = self.items.return_item(target)
                            try:
                                item(value, caller='Simulation')
                            except:
                                self.logger.error('Skipped unknown item: {}'.format(target))
                        entry = self.file.readline()
                        while entry == 'NextDay\n':                                                                                          # 0.7L Anpassung wegen Nextday, NextDay Kennung erkannt, If auf while geändert, falls mehrere Nextday Kennungen nacheinander folgen
                            entry = self.file.readline()
                            nextday = True                                                                                                   # 0.7L Anpassung wegen Nextday, NextDay Kennung erkannt, Info zwischenspeichern
                        if entry != '':
                            day = entry.split(';')[0]
                            time = entry.split(';')[1]
                            target = entry.split(';')[2]
                            value = entry.split(';')[3]
                            hour = int(time.split(':')[0])
                            minute = int(time.split(':')[1])
                            seconds = int(time.split(':')[2])
                            now = self.shtime.now()
                            next = now.replace(hour=hour, minute=minute, second=seconds)
                            dif = next - now
                            if (nextday == True):                                                                                            # 0.7L Anpassung wegen Nextday, Wurde NextDay erkannt, dann muss der Scheduler um einen Tag erhöht werden
                                self.logger.debug('Found next day {} {} {} shitfing to tomorrow.'.format(target, value, next))
                                next = next + timedelta(1)
                            self._message_item('Next event: {} - {}   {}'.format(time, target, value, 'Simulation'))                         # 0.7L Textausgabe ohne <br>, da dies in der Visu nicht als HTML sondern als Text angezeigt wird
                            self.logger.debug('Scheduling {} {} {}'.format(target, value, next))
                            self.scheduler_add('simulate', self._set_item, value={'target': target, 'value': value}, next=next)
                            self.lastday = day
                        else:
                            self.logger.info('End of file reached, simulation ended')
                            self._message_item('Simulation ended', 'Simulation')
                            self.state(0, 'Simulation')
                
                    # ------------------------------ windnuntil_now --------------------------------
                    # Reads the events from the file and skips until a time stamp is reached
                    # that is past the actual time
                
                    def _wind_until_now(self):
                        next = 0
                        now = 1
                        while next < now:
                            pos = self.file.tell()
                            entry = self.file.readline()
                            if entry == 'NextDay\n':
                                entry = self.file.readline()
                            if entry != '':
                                time = entry.split(';')[1]
                                hour = int(time.split(':')[0])
                                minute = int(time.split(':')[1])
                                seconds = int(time.split(':')[2])
                                now = self.shtime.now()
                                next = now.replace(hour=hour, minute=minute, second=seconds)
                                dif = next - now
                            else:
                                self.logger.info('End of file reached, simulation ended')
                                self._message_item('Simulation ended', 'Simulation')
                                self.state(0, 'Simulation')
                                return None
                        self.file.seek(pos)
                
                    # -------------------------------- do_nothing ----------------------------------
                    def _do_nothing(self):
                        self.logger.debug('Do nothing state: {} control: {}'.format(self.state(), self.control()))
                
                    # -------------------------------- _midnight ----------------------------------
                    # Called by the scheduler at midnight. It writes the NextDas keyword and
                    # removes the first day.
                
                    def _midnight(self):
                        self.logger.debug('Midnight')
                        if self.state() == 2:
                            self.file.write('NextDay\n')
                            self.file.flush()
                            if self.tank() > 13:
                                self._remove_first_day()
                            else:
                                tank = self.tank()
                                self.tank(tank + 1)
                
                    # -------------------------------- _get_tank ----------------------------------
                    # Returns the number of days in the  event file
                
                    def _get_tank(self):
                        self._lastentry = datetime.strptime('0:0:0', '%H:%M:%S')
                        try:
                            self.file = open(self._datafile, 'r')
                        except IOError as error:
                            self.logger.error('NoFile {}'.format(error))
                            return 0
                        entry = 'bla'
                        tank = 0;
                        while entry != '':
                            entry = self.file.readline()
                            if entry == 'NextDay\n':
                                tank = tank + 1
                            else:
                                if entry != '':
                                    self._lastentry = datetime.strptime(entry.split(';')[1], '%H:%M:%S')
                        self.file.close()
                        return tank
                
                    # ------------------------------ _remove_first_day ------------------------------
                    # Removes the forst day from the event file. It is called when the
                    # 15th day is finioshed at midnight.
                
                    def _remove_first_day(self):
                        self.logger.debug('Remove Day')
                        self.file.close()
                        #        try:
                        self.file = open(self._datafile, 'r')
                        #        except IOError as error:
                        #        self.logger.error('NoFile {}'.format(error))
                        #        try:
                        self.tempfile = open('/tmp/simulation.tmp', 'w')
                        #        except IOError as error:
                        #        self.logger.error('Canot open tempfile: {}'.format(error))
                        entry = 'bla'
                        while (entry != 'NextDay\n') and (entry != ''):
                            entry = self.file.readline()
                        while (entry != ''):
                            entry = self.file.readline()
                            self.tempfile.write(entry)
                        self.file.close()
                        self.tempfile.close()
                        self.tempfile = open('/tmp/simulation.tmp', 'r')
                        self.file = open(self._datafile, 'w')
                        entry = 'bla'
                        while (entry != ''):
                            entry = self.tempfile.readline()
                            self.file.write(entry)
                        self.file.close()
                        self.tempfile.close()
                        self.file = open(self._datafile, 'a')
                
                    # state_selector state, control
                    #                       (0,1): _remove_first_day,
                    state_selector = {(0, 0): _do_nothing,
                                      (0, 1): _do_nothing,
                                      (0, 2): _start_playback,
                                      (0, 3): _start_record,
                                      (1, 0): _do_nothing,
                                      (1, 1): _stop_recording,
                                      (1, 2): _do_nothing,
                                      (1, 3): _start_recording,
                                      (2, 0): _do_nothing,
                                      (2, 1): _stop_recording,
                                      (2, 2): _start_playback,
                                      (2, 3): _do_nothing,
                                      (4, 0): _do_nothing,
                                      (4, 1): _stop_playback,
                                      (4, 2): _do_nothing,
                                      (4, 3): _do_nothing, }
                
                    def _clear_file(self):
                        self.logger.debug('Clear File')
                        self.file.close()
                
                        self.file = open(self._datafile, 'w')
                        self.file.write('')
                        self.file.close()
                        self.tank(0)
                
                    def init_webinterface(self):
                        """"
                        Initialize the web interface for this plugin
                
                        This method is only needed if the plugin is implementing a web interface
                        """
                        try:
                            self.mod_http = Modules.get_instance().get_module(
                                'http')  # try/except to handle running in a core version that does not support modules
                        except:
                            self.mod_http = None
                        if self.mod_http == None:
                            self.logger.error("Plugin '{}': Not initializing the web interface".format(self.get_shortname()))
                            return False
                
                        # set application configuration for cherrypy
                        webif_dir = self.path_join(self.get_plugin_dir(), 'webif')
                        config = {
                            '/': {
                                'tools.staticdir.root': webif_dir,
                            },
                            '/static': {
                                'tools.staticdir.on': True,
                                'tools.staticdir.dir': 'static'
                            }
                        }
                
                        # Register the web interface as a cherrypy app
                        self.mod_http.register_webif(WebInterface(webif_dir, self),
                                                     self.get_shortname(),
                                                     config,
                                                     self.get_classname(), self.get_instance_name(),
                                                     description='')
                
                        return True
                
                    def get_items(self):
                        return self._items
                
                
                # ------------------------------------------
                #    Webinterface of the plugin
                # ------------------------------------------
                
                import cherrypy
                from jinja2 import Environment, FileSystemLoader
                
                
                class WebInterface(SmartPluginWebIf):
                
                    def __init__(self, webif_dir, plugin):
                        """
                        Initialization of instance of class WebInterface
                
                        :param webif_dir: directory where the webinterface of the plugin resides
                        :param plugin: instance of the plugin
                        :type webif_dir: str
                        :type plugin: object
                        """
                        self.logger = logging.getLogger(__name__)
                        self.webif_dir = webif_dir
                        self.plugin = plugin
                
                        self.tplenv = self.init_template_environment()
                
                    @cherrypy.expose
                    def index(self, cmd=None, reload=None):
                        """
                        Build index.html for cherrypy
                
                        Render the template and return the html file to be delivered to the browser
                
                        :return: contents of the template after beeing rendered
                        """
                        data_file_content = []
                        if cmd == 'delete_data_file':
                            if len(self.plugin._datafile) > 0:
                                self.plugin._clear_file()
                        elif cmd == 'show_data_file':
                            try:
                                file = open(self.plugin._datafile, 'r')
                
                                for line in file:
                                    data_file_content.append(line)
                                file.close()
                            except IOError as error:
                                self.logger.error('NoFile {}'.format(error))
                
                        start_record = self.plugin.scheduler_get('startrecord')
                        simulate = self.plugin.scheduler_get('simulate')
                        start_record_entry = None
                        simulate_entry = None
                        if start_record is not None:
                            start_record_entry = start_record['next']
                        if simulate is not None:
                            simulate_entry = simulate['next']
                
                        tmpl = self.tplenv.get_template('index.html')
                        return tmpl.render(plugin_shortname=self.plugin.get_shortname(), plugin_version=self.plugin.get_version(),
                                           interface=None, item_count=len(self.plugin.get_items()),
                                           plugin_info=self.plugin.get_info(), tabcount=1, startRecord=start_record_entry,
                                           simulate=simulate_entry,
                                           cmd=cmd, data_file_content=data_file_content,
                                           tab1title="Simulation Items (%s)" % len(self.plugin.get_items()),
                                           p=self.plugin)


                Zwei Kleinigkeiten noch:
                1. Ganz oben steht "# 1!/usr/bin/env python3". Muss die 1 da hin?
                2. Nach 14 Tagen ist Schluss mit dem Replay. Kann man das leicht ändern und einfach neu starten?
                Das Plugin zeichnet standardmäßig auch 14 Tage auf und wenn die voll sind, dann wird irgendwann der erste Tag verworfen und alles rutscht einen durch (Umlaufpuffer). So ist es auch kein Problem zwischen Sommer und Winter. Die Zeiten passen sich ja so langsam an (früher Licht einschalten im Winter usw.).

                Beim Abspielen wird aber einfach nach 14 Tagen beendet. Ist die Aufzeichnungsdatei zu Ende, dann hört Replay einfach auf. Vielleicht kannst Du das auch einfach verändern, dass es einfach wieder von vorne startet. Das fände ich noch toll. Kenne mich hier aber nicht gut genug aus, ob ich folgende Änderung einfach machen kann.

                Das ist auch in dem Codeabschnitt "_set_item" ganz am Ende. Das wird einfach der State auf 0 gesetzt. Vielleicht siehst Du hier auch eine einfache Möglichkeit einfach wieder von vorne zu beginnen (quasi ein Restart). Hier ist mir bisher noch nichts tolles zu eingefallen.

                Code:
                        else:
                            self.logger.info('End of file reached, simulation ended')
                            self._message_item('Simulation ended', 'Simulation')
                            self.state(0, 'Simulation')
                Danke Dir!

                Gruß
                loeserman
                Zuletzt geändert von loeserman; 01.01.2019, 13:26.

                Kommentar


                  #68
                  danke für die ausführungen.

                  ganz ehrlich, glaube ich nicht, dass sich der autor gedanken darüber gemacht hat, was ist, wenn jemand die aufzeichnung absichtlich ausschaltet und paar stunden später wieder an. ich vermute, dass das plugin dafür gedacht war, einmal gestartet zu werden und dann einfach mitzuschneiden. aus der sicht sehe ich eine änderung an der stelle auch etwas kritisch. vor allem könntest du ein zeitliches durcheinander kriegen. in deinem beispiel ist der nächste samstags-zeitpunkt (wenn man den tag ignoriert) chronologisch nach dem letzten freitags zeitpunkt.
                  Code:
                  Fr;10:06:37;R02.LICHT.DECKE;True;KNX
                  Fr;10:07:43;R02.LICHT.DECKE;False;KNX
                  Sa;10:11:10;R12.LICHT.GARDEROBE;True;KNX
                  Sa;10:11:11;R12.LICHT.GARDEROBE;False;KNX
                  wenn ich den tag ignoriere und nur auf "tagesscheiben" setze, was wäre dann im fall (man beachte die 10:01:10 anstatt 10:11:10):
                  Code:
                  Fr;10:06:37;R02.LICHT.DECKE;True;KNX
                  Fr;10:07:43;R02.LICHT.DECKE;False;KNX
                  Sa;10:01:10;R12.LICHT.GARDEROBE;True;KNX
                  Sa;10:11:11;R12.LICHT.GARDEROBE;False;KNX
                  das plugin liest soweit ich das sehe mit readline ein, also zeile für zeile und arbeitet so das ganze auch ab. um "tagesscheiben" in deiner denke zu unterstützen, müsste es alles einlesen und zeitlich (unter ignorieren des tages) neu sortieren. das passiert aber nicht.

                  das sind sicher gute gedanken für eine grundlegende überarbeitung, die ich aber nicht leisten werde. oder verstehe ich da was falsch?

                  zu
                  1. Ganz oben steht "# 1!/usr/bin/env python3". Muss die 1 da hin?
                  das ist nur ein kommentar, der kann auch ganz raus

                  zu 2) auch das wäre ein issue für eine grundlegende überarbeitung.. das plugin ist halt doch noch relativ proprietär.. meiner meinung nach sollten änderungen wenn aber auch architekturell vollständig sinnvoll durchdacht werden.. ich schaue mir den diff aber auf jdn fall noch an
                  Zuletzt geändert von psilo; 01.01.2019, 13:43.

                  Kommentar


                    #69
                    Danke für die Rückmeldungen.

                    ganz ehrlich, glaube ich nicht, dass sich der autor gedanken darüber gemacht hat, was ist, wenn jemand die aufzeichnung absichtlich ausschaltet und paar stunden später wieder an.
                    Also das Stop, von dem ich geschrieben habe bedeutet nicht, dass das Plugin selbst gestoppt wird (wie über die Weboberfläche im Backend) sondern das ist ein interner Zustand des Zustandsautomaten. Ein typischer Fall ist ja, dass das Plugin aufzeichnet und man dann das Haus verlässt. Dann schalte ich automatisch von Record auf Play und wenn ich zurückkomme schalte ich zurück auf Record. Das geht auch gut.er Autor hat sich hierzu auch schon seine Gedanken gemacht. Anbei der Auszug aus seiner Beschreibung.

                    Record
                    The plugin starts automatically together with smarthome.py. After initialization it automatically starts to record all changes to items that have the sim: track in the item.yaml file. Item datatypes bool and num have been tested. When an item is changed it is called an event. All events are stored in a text file. It does not matter where the change is initiated from with one exception: The plugin does not record events that are triggered by the plugin itself when it is in playback mode. When the plugin initializes for the first time, the event file is created. On all subsequent starts events are appended to the existing file. The plugin records a maximum of 14 days. When the 15th day is over, the first day is deleted. So the file always contains the recent 14 days plus the rest of today. When recording starts, either after startup or after setting control to 03, it does not start immediately. In case the event file is empty, recording will start at next midnight. Until midnight the plugin will be in stand-by. By that there will always be a full day in the file. In case the plugin finds events in the file, it compares the last recorded event with the actual time. In case the actual time is max. 15 minutes advance the last recorded event, recording will start immediately. In case the actual time is more advance, recording will start one minute after the last event on the next day. Ba this behavior empty gaps in the event file are avoided when recording was stopped for some time because .eg. playback was active. If control is set to 01, recording stops immediately.
                    Quelle: https://github.com/smarthomeNG/plugi...ter/simulation


                    Zu Deinem Punkt mit 10:01:10
                    wenn ich den tag ignoriere und nur auf "tagesscheiben" setze, was wäre dann im fall (man beachte die 10:01:10 anstatt 10:11:10):
                    Das kann es nicht geben. Wenn der letzte Eintrag 10:07:43 ist, dann startet die Aufzeichung auch erst wieder ab dieser Zeit. Der Autor hat hierzu eine 15min Prüfung eingeführt. Schaltet man vor 10:22:43 wirder auf Record wird ab da sofort wieder augezeichnet (<15min zum letzten gespeicherten Even). Schaltet man nach 10:22:43 wieder auf Record (>15min zum letzten gespeicherten Event) geht die Aufzeichnung erst am folgenden Tag ab 10:08:43 (letztes Event + 1 Minute) wieder los. Es kann daher nicht sein, dass hier ein kleinerer Uhrzeitstempel als der letzte auftaucht.
                    Zuletzt geändert von loeserman; 01.01.2019, 14:05.

                    Kommentar


                      #70
                      ja mir war schon klar, dass das plugin nicht mit stop() gestoppt wird. der 15 minuten check ist mir aber neu.. ich schaue das nochmal in ruhe an und dann würde m.e. deinen änderungen nichts im weg stehen.

                      Kommentar


                        #71
                        Alles klar. Danke Dir. Eilt ja nicht ...

                        Kommentar


                          #72
                          Jetzt wollte ich das Simulations Plugin aus gegebenen Anlass mal nutzen, da bekomme ich beim Abspielen diese Meldung im Log:
                          Code:
                          2019-07-16  23:23:46 ERROR    Main         Item sim.control: problem running <bound method Simulation.update_item of <plugins.simulation.Simulation object at 0x677beab0>>: list index out of range
                          Traceback (most recent call last):
                            File "/usr/local/smarthome/lib/item.py", line 2224, in __update
                              method(self, caller, source, dest)
                            File "/usr/local/smarthome/plugins/simulation/__init__.py", line 129, in update_item
                              self.state_selector[self.state(), self.control()](self)
                            File "/usr/local/smarthome/plugins/simulation/__init__.py", line 191, in _start_playback
                              self._wind_until_now()
                            File "/usr/local/smarthome/plugins/simulation/__init__.py", line 266, in _wind_until_now
                              time = entry.split(';')[1]
                          IndexError: list index out of range
                          Kann sich das jemand erklären oder ist das bei euch evtl. auch so?

                          Kommentar


                            #73
                            schuma ohne deine zugehoerige aufzeichnung zu sehen vermutlich nein ;>

                            logge halt mal raus was in entry an der stelle steht.

                            Kommentar


                              #74
                              Hier mal meine Aufzeichnungen:
                              Code:
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              Sa;22:01:49;EG.Raum7.Licht.Deckenspots;True;Visu
                              Sa;22:01:51;EG.Raum7.Licht.Deckenspots;False;Visu
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              NextDay
                              Sa;10:13:46;EG.Raum1.Licht.Wand;True;Visu
                              Sa;10:13:54;EG.Raum1.Licht.Wand;False;Visu
                              NextDay
                              Was aber auch nicht viel ist. Ich würde sagen da wird nichts aufgezeichnet. Mal schauen...

                              Ich habe jetzt mal die Caller auskommentiert.
                              Wir gucken was passiert...

                              Kommentar


                                #75
                                Naja es steht ja was drin das ist mehr als nichts. Items ggf für die Aufzeichnung falsch konfiguriert (wenn es mehr sein sollten?)?

                                Sieht fast so aus, als würde er das NextDay zerlegen wollen. Ich teste mal..

                                PS: siehst du die items die aufzeichnen sollen im webinterface des plugins?

                                Evtl fehlt bei deinem NextDay ein "\n", das ist irgendwie so gelöst:
                                Code:
                                if entry == 'NextDay\n':
                                    entry = self.file.readline()
                                if entry != '':
                                    time = entry.split(';')[1]
                                Wenns so ist: keine Ahnung. Logge wie gesagt raus, was nach dem "if entry != '':" im entry steht..
                                Zuletzt geändert von psilo; 17.07.2019, 11:21.

                                Kommentar

                                Lädt...
                                X