Ankündigung

Einklappen
Keine Ankündigung bisher.

Interactive Shell und CLI kombiniert

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

    [Featurewunsch] Interactive Shell und CLI kombiniert

    Hallo,

    oft würde mir ein Zwischending zwischen der Interacitve Shell (-i) und dem CLI Plugin helfen.
    Beispiel: Das Whatsapp Plugin funktioniert nicht. Da wäre es hilfreich einfach mal ein paar Methoden des Plugins aufrufen zu können was mit der Interactive Shell kein Problem ist, mit der CLI hingegen nicht möglich ist.
    Um in die Interactive Shell zu gelangen muss ich smarthome.py jedoch neu starten. Dann ist das Whatsapp Problem aber auch nicht mehr vorhanden, da es nur nach längerer Laufzeit auftritt.

    Meine Idee war die Interactive Shell um eine Remote Interactive Shell zu erweitern. Im Prinzip einfach STDOUT und STDIN über einen Netzwerksocket zur Verfügung stellen.

    Dazu habe ich auch schon ein Stück Coding im Web gefunden was nach einigen Anpassungen auch ganz passabel läuft:
    Code:
    #!/usr/bin/env python3
    import sys
    import code
    import socket
    
    globals()['sockfd0'] = ''
    globals()['sockfd'] = ''
    globals()['sock'] = ''
    
    
    
    def read_cmd(var):
    #    print("Reading {0}".format(var))
        sockfd = globals()['sockfd']
        try:
            globals()['sock'].send(var.encode('utf-8')) # prompt
        except socket.error as msg:
            conn()
        line = sockfd.readline()
        return line
    # end read_cmd
    
    def write_cmd(self, data):
    #    print("Writing {0}".format(data))
        try:
            globals()['sock'].send(data.encode('utf-8'))
        except socket.error as msg:
            conn()
    # end write_cmd
    
    class wsock():
        def __init__(self, sock):
            self.__sock = sock
        # end __init__
    
        def write(self, text):
        try:
            return self.__sock.send(text.encode('utf-8'))
        except socket.error as msg:
            conn()
        # end write
    # end class
    
    def init():
        port = 2001
    
        globals()['sockfd0'] = socket.socket()
        globals()['sockfd0'].setsockopt(socket.SOL_SOCKET , socket.SO_REUSEADDR , 1)
        globals()['sockfd0'].bind(('',port))
        globals()['sockfd0'].listen(1)
        # got connection
    
    def conn():
        cs , caddr = globals()['sockfd0'].accept()  
    
        globals()['sockfd'] = cs.makefile()
        globals()['sock'] = cs
        sys.stdout = wsock(cs) # hack much?
        code.InteractiveInterpreter.write = write_cmd
        code.interact(local=locals(), readfunc=read_cmd)
    # end bind_iaps
    
    init()
    conn()
    
    import time
    while(True):
        time.sleep(5)
    Durch den Befehl nc localhost 2001 kann man nun auf eine Interactive Shell zugreifen. Auch Trennen und Wiederverbinden funktioniert schon.

    Vielleicht findet das ja noch der ein oder andere Interessant und es findet seinen Weg in den Dev Branch. Den wirklichen Einbau würde ich dann allerdings Marcus überlassen wollen da das Ganze wohl in der Smarthome.py verankert werden müsste. Parameter -r für Remote Interactive Shell ist ja noch frei ;-)

    LG

    Mode

    #2
    Hi Daniel,

    nett.
    Ich kann das auf meine Feature-Liste nehmen.
    Vorher habe ich aber noch ein paar andere Dinge zu erledigen.

    Ich sehe es entweder als eigenständiges Plugin oder als Erweiterung zum CLI.
    Eigenständiges Plugin könntest Du ja auch einfach machen.

    Bis bald

    Marcus

    Kommentar


      #3
      Und da ein eigenständiges Plugin in einem eigenen Thread läuft hat das Umbiegen des stout auch keine Folgen für den Rest der Smarthome.py Instanz?

      Kommentar


        #4
        Sicher? Du gibst doch explizit die Methoden an, die für In als auch Output verwendet werden sollen.

        Kommentar


          #5
          Ich hab gestern schon mal was zusammengeschrieben
          Code:
          #!/usr/bin/env python3
          # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
          #########################################################################
          # Copyright 2013 KNX-User-Forum e.V.            https://knx-user-forum.de/
          #########################################################################
          #  This file is part of SmartHome.py.    http://mknx.github.io/smarthome/
          #
          #  SmartHome.py 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.
          #
          #  SmartHome.py 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 SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
          #########################################################################
          
          import logging
          import sys
          import code
          import socket
          
          logger = logging.getLogger('RemoteInteractiveShell')
          
          class wsock():
              def __init__(self, sock):
                  self.__sock = sock
              # end __init__
          
              def write(self, text):
                  try:
                      return self.__sock.send(text.encode('utf-8'))
                  except socket.error as msg:
                      self._create_socket()
          
          class RemoteInteractiveShell():
          
              def __init__(self, smarthome):
                  self._sh = smarthome
                  self._create_socket()
                  self._accept_connection()
                  self.alive = False
          
              def _create_socket(self, port = 2001):
                  self._port = port
                  self._socket = socket.socket()
                  self._socket.setsockopt(socket.SOL_SOCKET , socket.SO_REUSEADDR , 1)
                  self._socket.bind(('', self._port))
                  self._socket.listen(1)
          
              def _accept_connection(self):
                  cs , caddr = self._socket.accept()  
          
                  self._sockfd = cs.makefile()
                  self._connection = cs
                  sys.stdout = wsock(cs) # hack much?
                  code.InteractiveInterpreter.write = self._write_cmd
                  code.interact(local=locals(), readfunc = self._read_cmd)
          
              def _read_cmd(self, var):
              #    print("Reading {0}".format(var))
                  try:
                      self._connection.send(var.encode('utf-8')) # prompt
                  except socket.error as msg:
                      if self.alive:
                          self._accept_connection()
                  line = self._sockfd.readline()
                  return line
              # end read_cmd
          
              def _write_cmd(self, data):
              #    print("Writing {0}".format(data))
                  try:
                      self._connection.send(data.encode('utf-8'))
                  except socket.error as msg:
                      if self.alive:
                          self._accept_connection()
              # end write_cmd
                  
              def run(self):
                  self.alive = True
                  # 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.alive = False
                  self._sockfd.close()
                  self._connection.close()
                  self._socket.close()
          
              def parse_item(self, item):
                  return None
          
              def parse_logic(self, logic):
                  pass
          
              def update_item(self, item, caller=None, source=None, dest=None):
                  pass
                  
          if __name__ == '__main__':
              logging.basicConfig(level=logging.DEBUG)
              myplugin = Plugin('RemoteInteractiveShell')
              myplugin.run()
          Es fehlt noch die Übergabe der lokalen Variablen (sh). Habe an der Stelle aber erstmal nicht weitergemacht, da hier der Stdout von der ganzen sh Instanz umgeleitet wird und das Logfile ab Start des Plugins keine Daten mehr bekommt. Betrachtet dies also mehr als eine Idee als ein fertiges Plugin ;-)

          Kommentar


            #6
            Hi. könnte man noch integrieren, dass man den Loglevel (Info, Warn, etc..) setzen kann?
            Ich habs gestern mal via CLI versucht, jedoch ohne Erfolg.

            Gruss
            Buffi

            Kommentar

            Lädt...
            X