Ankündigung

Einklappen
Keine Ankündigung bisher.

Unterstützung bei der Entwicklung eines Landroid-Plugin

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

    Unterstützung bei der Entwicklung eines Landroid-Plugin

    Hallo,

    für meinen neuen Rasenmähroboter gib es eine Python-Bibliothek (pyworxcloud).
    Diese habe ich so modifiziert, dass sie kein asyncio benötigt. Ich habe meine modifizierte Bibliothek im plugin-verzeichnis platziert.
    Dann habe ich das sample-plugin als Startpunkt genommen und habe begonnen das Plugin zu erstellen. Wenn ich das Plugin nun aktiviere, habe ich jedoch das Problem, dass alle Plugins außer influx nicht mehr starten. Im Log sind keine Fehler zu sehen.

    Ich habe dann möglichst alles was Probleme machen kann auskommentiert. Selbst so läuft es nicht mehr:
    Code:
    #!/usr/bin/env python3
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #########################################################################
    #  Copyright 2020-      <AUTHOR>                                  <EMAIL>
    #########################################################################
    #  This file is part of SmartHomeNG.
    #  https://www.smarthomeNG.de
    #  https://knx-user-forum.de/forum/supportforen/smarthome-py
    #
    #  Sample plugin for new plugins to run with SmartHomeNG version 1.5 and
    #  upwards.
    #
    #  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/>.
    #
    #########################################################################
    
    from lib.model.smartplugin import *
    from lib.item import Items
    
    from .webif import WebInterface
    
    #import sys
    #sys.path.append(".")
    #import py#xworxcloud
    
    
    # If a needed package is imported, which might be not installed in the Python environment,
    # add it to a requirements.txt file within the plugin's directory
    
    
    class SamplePlugin(SmartPlugin):
        """
        Main class of the Plugin. Does all plugin specific stuff and provides
        the update functions for the items
        """
    
        PLUGIN_VERSION = '1.0.0'    # (must match the version specified in plugin.yaml), use '1.0.0' for your initial plugin Release
    
        def __init__(self, sh):
            """
            Initalizes the plugin.
    
            If you need the sh object at all, use the method self.get_sh() to get it. There should be almost no need for
            a reference to the sh object any more.
    
            Plugins have to use the new way of getting parameter values:
            use the SmartPlugin method get_parameter_value(parameter_name). Anywhere within the Plugin you can get
            the configured (and checked) value for a parameter by calling self.get_parameter_value(parameter_name). It
            returns the value in the datatype that is defined in the metadata.
            """
    
            # Call init code of parent class (SmartPlugin)
            super().__init__()
    
            # get the parameters for the plugin (as defined in metadata plugin.yaml):
            # self.param1 = self.get_parameter_value('param1')
    
            # cycle time in seconds, only needed, if hardware/interface needs to be
            # polled for value changes by adding a scheduler entry in the run method of this plugin
            # (maybe you want to make it a plugin parameter?)
            self._cycle = 60
            
            #self.#xworx = py#xworxcloud.#xworxCloud()
    
            # Initialize connection, using your #xworx email and password
            #auth =  #xworx.initialize("xx","yy")
            auth = False
    
            if not auth:
                #If invalid credentials are used, or something happend during
                #authorize, then exit
                self.logger.error("Authentication failed")  
                # On initialization error use:
                self._init_complete = False
                return
    
    
            #Connect to device with index 0 (devices are enumerated 0, 1, 2 ...) and do
            #not verify SSL (False)
            #xworx.connect(0, True)            
    
            # Initialization code goes here
    
    
    
            # if plugin should start even without web interface
            #self.init_webinterface(WebInterface)
            # if plugin should not start without web interface
            # if not self.init_webinterface():
            #     self._init_complete = False
    
            return
    
        def run(self):
            """
            Run method for the plugin
            """
            self.logger.debug("Run method called")
            # setup scheduler for device poll loop   (disable the following line, if you don't need to poll the device. Rember to comment the self_cycle statement in __init__ as well)
            self.scheduler_add('poll_device', self.poll_device, cycle=self._cycle)
            
            self.alive = True
            # if you need to create child threads, do not make them daemon = True!
            # They will not shutdown properly. (It's a python bug)
    
        def stop(self):
            """
            Stop method for the plugin
            """
            self.logger.debug("Stop method called")
            self.scheduler_remove('poll_device')
            self.alive = False
    
        def parse_item(self, item):
            """
            Default plugin parse_item method. Is called when the plugin is initialized.
            The plugin can, corresponding to its attribute keywords, decide what to do with
            the item in future, like adding it to an internal array for future reference
            :param item:    The item to process.
            :return:        If the plugin needs to be informed of an items change you should return a call back function
                            like the function update_item down below. An example when this is needed is the knx plugin
                            where parse_item returns the update_item function when the attribute knx_send is found.
                            This means that when the items value is about to be updated, the call back function is called
                            with the item, caller, source and dest as arguments and in case of the knx plugin the value
                            can be sent to the knx with a knx write function within the knx plugin.
            """
            if self.has_iattr(item.conf, 'foo_itemtag'):
                self.logger.debug("parse item: {}".format(item))
    
            # todo
            # if interesting item for sending values:
            #   return self.update_item
    
        def parse_logic(self, logic):
            """
            Default plugin parse_logic method
            """
            if 'xxx' in logic.conf:
                # self.function(logic['name'])
                pass
    
        def update_item(self, item, caller=None, source=None, dest=None):
            """
            Item has been updated
    
            This method is called, if the value of an item has been updated by SmartHomeNG.
            It should write the changed value out to the device (hardware/interface) that
            is managed by this plugin.
    
            :param item: item to be updated towards the plugin
            :param caller: if given it represents the callers name
            :param source: if given it represents the source
            :param dest: if given it represents the dest
            """
            if self.alive and caller != self.get_shortname():
                # code to execute if the plugin is not stopped
                # and only, if the item has not been changed by this this plugin:
                self.logger.info("Update item: {}, item has been changed outside this plugin".format(item.id()))
    
                if self.has_iattr(item.conf, 'landroid'):
                    self.logger.debug("update_item was called with item '{}' from caller '{}', source '{}' and dest '{}'".format(item,
                                                                                                    caller, source, dest))
                    
                    if self.get_iattr_value(item.conf, 'landroid')=='rain_delay':          
                        #Set rain delay to 60 minutes
                        #xworx.setRainDelay(60)
                        pass
    
                    if self.get_iattr_value(item.conf, 'landroid')=='start':    
                        #Start mowing routine
                        #xworx.start()
                        pass
    
                    if self.get_iattr_value(item.conf, 'landroid')=='pause':    
                        #Pause mowing routine
                        #xworx.pause()
                        pass
    
                    if self.get_iattr_value(item.conf, 'landroid')=='stop':    
                        #Stop and return to base
                        #xworx.stop()    
                        pass                    
                                                          
    
    
        def poll_device(self):
            """
            Polls for updates of the device
    
            This method is only needed, if the device (hardware/interface) does not propagate
            changes on it's own, but has to be polled to get the actual status.
            It is called by the scheduler which is set within run() method.
            """
            # # get the value from the device
            # device_value = ...
            #
            # # find the item(s) to update:
            # for item in self.sh.find_items('...'):
            #
            #     # update the item by calling item(value, caller, source=None, dest=None)
            #     # - value and caller must be specified, source and dest are optional
            #     #
            #     # The simple case:
            #     item(device_value, self.get_shortname())
            #     # if the plugin is a gateway plugin which may receive updates from several external sources,
            #     # the source should be included when updating the the value:
            #     item(device_value, self.get_shortname(), source=device_source_id)
            
            #Force and update request to get latest state from the device
            #xworx.update()
            
            #Read latest states received from the device
            #xworx.getStatus()
            
            #Print all attributes received from the device
            #attrs = vars(#xworx)
            #for item in attrs:
            #    self.logger.warning(item , ':' , attrs[item])
    Woran liegt das? Ich hätte jetzt erwartet, dass ich wenigstens eine Fehlermeldung bekomme?

    Gruß,
    Hendrik

    #2
    Anhand von dem was Du gepostet hast sehe ich auch keinen Grund wieso das bei Dir nicht mehr läuft. Was hast Du sonst den noch getan? Wie sehen die Metadatan zu Deinem Plugin aus? Bekommst Du evtl. schon einen Fehler bevor SmartHomeNG in den Hintergrund geht? Wie sieht denn die Terminalausgabe aus, wenn Du SmartHomeNG vom Prompt startest?
    Viele Grüße
    Martin

    There is no cloud. It's only someone else's computer.

    Kommentar


      #3
      Zitat von henfri Beitrag anzeigen
      Woran liegt das? Ich hätte jetzt erwartet, dass ich wenigstens eine Fehlermeldung bekomme?
      Aus ähnlicher Erfahrung kann ich dir empfehlen, bei der Entwicklung von Plugins shNG im Debug-Modus zu starten.

      Dafür habe ich immer den Dienst gestoppt bzw. disabled
      Code:
      sudo systemctl disable smarthome.service
      und dann aus einem Terminal shNG im debug Modus gestartet.

      Code:
      cd /usr/local/smarthome/bin
      python3 smarthome.py -d
      Im Terminal findest Du dann alle Ausgaben. Dort sollten auch die Meldungen erscheinen, warum das Plugin nicht startet.

      In einem zweiten Terminal kannst Du dann shNG beenden und ggf. -wieder im Debug-Modus- neu starten.

      Code:
      python3 smarthome.py -s
      Wenn Du mit allen fertig bist, den Dienst wieder enablen und neu starten.

      Ich war auch der Meinung, dass alle Meldungen im Log landen. Dem ist aber nichts so.
      Vielleicht hilfts.

      Kommentar


        #4
        Abgesehen davon, dass bei Dir das Rumpf-Plugin nicht mehr funktioniert ist folgendes eine schlechte Idee:

        Code:
        #sys.path.append(".")
        #import py#xworxcloud
        1. sys.path.append sollte man nicht nutzen, Damit versucht Python bei jedem Import (Core und alle Plugins), ob die gewünschte Datei in Deinem Plugin liegt
        2. Was soll py#xworxcloud tun?

        Statt mit sys.path.append(".") machst Du den Import am einfachsten so:

        Code:
        import pyxworxcloud
        Du musst, falls das Package aus mehreren Dateien besteht, natürlich die imports innerhalb von pyxworxcloud auch anpassen.
        Viele Grüße
        Martin

        There is no cloud. It's only someone else's computer.

        Kommentar


          #5
          Hallo,

          das mit dem Import war ein schneller hack, ja.
          das py#xworxcloud kommt aus einem seach and replace von worx durch #xworx den ich dann später wieder rückgängig machen will/wollte. Zweck: Sicherstellen, dass es nicht an der Bibiliothek und dessen Nutzung (fokus war hier das worx objekt an mehreren Stellen; dieser import war nur kollateralschaden.

          Ich schau mal, was an der Kommandozeile zu sehen ist.

          Die plugin.yaml sieht so aus:
          Code:
          # Metadata for the plugin
          plugin:
              # Global plugin attributes
              type: interface                   # plugin type (gateway, interface, protocol, system, web)
              description:
                  de: 'Plugin für die Steuerung eines Worx Landroid'
                  en: 'Plugin controlling Worx Landroid bots'
              maintainer: Hendrik Friedel
          #    tester:                         # Who tests this plugin?
              state: develop                  # change to ready when done with development
          #    keywords: iot xyz
          #    documentation: https://github.com/smarthomeNG/smarthome/wiki/CLI-Plugin        # url of documentation (wiki) page
          #    support: https://knx-user-forum.de/forum/supportforen/smarthome-py
          
              version: 1.0.0                  # Plugin version (must match the version specified in __init__.py)
              sh_minversion: 1.8              # minimum shNG version to use this plugin
          #    sh_maxversion:                 # maximum shNG version to use this plugin (leave empty if latest)
          #    py_minversion: 3.6             # minimum Python version to use for this plugin
          #    py_maxversion:                 # maximum Python version to use for this plugin (leave empty if latest)
              multi_instance: False           # plugin supports multi instance
              restartable: true
              classname: landroid         # class containing the plugin
          
          parameters:
              # Definition of parameters to be configured in etc/plugin.yaml (enter 'parameters: NONE', if section should be empty)
              param1:
                  type: str
                  description:
                      de: 'Demo Parameter'
                      en: 'Parameter for demonstration purposes'
          
              param2:
                  type: str
                  default: 'value2'
                  valid_list:
                      - 'value1'
                      - 'value2'
                      - 'value3'
                      - 'value4'
                      - 'value5'
                  description:
                      de: 'Demo Parameter mit Gültigkeitsliste und Standardwert'
                      en: 'Demonstration parameter with valid-list and default value'
          
              param3:
                  type: str
                  # If 'mandatory' is specified, a 'default' attribute must not be specified
                  mandatory: True
                  description:
                      de: 'Demo Parameter der angegeben werden muss'
                      en: 'Demonstration parameter which has to be specified'
          
          item_attributes:
              - landroid
              # Definition of item attributes defined by this plugin (enter 'item_attributes: NONE', if section should be empty)
          
          item_structs:
              # Definition of item-structure templates for this plugin (enter 'item_structs: NONE', if section should be empty)
              - NONE
          #item_attribute_prefixes:
              # Definition of item attributes that only have a common prefix (enter 'item_attribute_prefixes: NONE' or ommit this section, if section should be empty)
              # NOTE: This section should only be used, if really nessesary (e.g. for the stateengine plugin)
          
          
          plugin_functions:
              # Definition of plugin functions defined by this plugin (enter 'plugin_functions: NONE', if section should be empty)
              - NONE
          logic_parameters:
              # Definition of logic parameters defined by this plugin (enter 'logic_parameters: NONE', if section should be empty)
              - NONE
          Gruß,
          Hendrik

          Kommentar


            #6
            Hallo,

            so, ich hab den Grund gefunden:
            Code:
            2021-07-10 21:56:06 ERROR lib.smarthome Unhandled exception: 'list' object has no attribute 'keys'
            <class 'AttributeError'>
            File "smarthome.py", line 275, in <module>
            sh.start()
            File "/usr/local/smarthome/lib/smarthome.py", line 615, in start
            self.plugins = lib.plugin.Plugins(self, configfile=self._plugin_conf_basename)
            File "/usr/local/smarthome/lib/plugin.py", line 119, in __init__
            plugin_name, self.meta = self._get_pluginname_and_metadata(plugin, _conf[plugin])
            File "/usr/local/smarthome/lib/plugin.py", line 221, in _get_pluginname_and_metadata
            meta = Metadata(self._sh, (plugin_name+plugin_version).replace('.',os.sep), 'plugin')
            File "/usr/local/smarthome/lib/metadata.py", line 142, in __init__
            self._itemdeflist = list(self.itemdefinitions.keys())
            Das habe ich durch das Ändern der Plugin.yaml gelöst:
            Code:
            item_attributes: NONE
            Den part habe ich wohl noch nicht verstanden.

            Aber ich denke, ich komme jetzt erstmal weiter. Danke!

            Gruß,
            Hendrik

            Kommentar


              #7
              Hallo,

              import pyworxcloud
              Du musst, falls das Package aus mehreren Dateien besteht, natürlich die imports innerhalb von pyxworxcloud auch anpassen.
              Ich scheitere gerade am import von pyworxcloud.
              Ich habe in meinem Pluginverzeichnis einen Ordner "pyworxcloud". Darin befindet sich eine __init__.py und eine worxcloudapi.py. Diese wird in der init.py so importiert
              Code:
              from .worxlandroidapi import *
              Der Fehler lautet:
              Code:
               Plugin 'my_landroid' error importing Python package: No module named 'pyworxcloud'
              Wo habe ich dich falsch verstanden?

              Gruß,
              Hendrik

              Kommentar


                #8
                Hallo,

                es ist komisch... Lokal in meiner example.py funktioniert es mit import pyworxcloud.
                Im plugin geht es nicht.
                So ist meine Struktur im Plugin:
                Code:
                .
                ├── example.py
                ├── __init__.py
                ├── locale.yaml
                ├── plugin.yaml
                ├── pyworxcloud
                │   ├── __init__.py
                │   └── worxlandroidapi.py
                ├── requirements.txt
                ├── user_doc.rst
                └── webif
                ├── __init__.py
                ├── static
                │   └── img
                │   └── readme.txt
                └── templates
                └── index.html
                So in meinem Beispiel:
                Code:
                ├── build
                │   └── lib
                │   └── pyworxcloud
                │   ├── __init__.py
                │   └── worxlandroidapi.py
                ├── example.py
                ├── Files
                │   ├── Codes
                │   │   ├── Error.txt
                │   │   └── Status.txt
                │   ├── Idea URL.txt
                │   ├── Requirements.txt
                │   └── Response
                │   ├── api_response.json
                │   └── mqtt_response.json
                ├── pyworxcloud
                │   ├── __init__.py
                │   └── worxlandroidapi.py
                ├── README.md
                ├── setup.py
                └── update.cmd
                Abgesehen von einigen *.txt und der setup.py sehe ich jetzt keinen Unterschied...?

                Gruß,
                Hendrik

                Kommentar


                  #9
                  Hallo,

                  ich bin ein Stück weiter, aber weiter komme ich nicht..
                  Ich habe es vom Sonos Plugin abgeschaut.

                  Code:
                  import plugins.landroid.pyworxcloud
                  #from plugins.landroid.pyworxcloud import *
                  from plugins.landroid.pyworxcloud import worxCloud
                  Das führt aber zu:
                  Code:
                  2021-07-11 20:55:49 ERROR lib.plugin Plugin 'my_landroid' error importing Python package: cannot import name 'worxCloud' from 'plugins.landroid.pyworxcloud' (/usr/local/smarthome/plugins/landroid/pyworxcloud/__init__.py)
                  2021-07-11 20:55:49 ERROR lib.plugin Plugin 'my_landroid' initialization failed, plugin not loaded
                  Das verstehe ich nicht.

                  Im Sonos-Plugin wird genauso aus soco eine Klasse importiert.
                  Die __init__.py in pyworxckoud sieht so aus:
                  Code:
                  import concurrent.futures
                  import contextlib
                  import time
                  
                  from .worxlandroidapi import *
                  [...]
                  class WorxCloud:
                  Dieser relative import aus der worxlandroidapi.py sollte doch auch kein Problem sein, oder?

                  Gruß,
                  Hendrik

                  Kommentar


                    #10
                    Hallo henfri ,

                    da es zu pyworxcloud ein pip package gibt würde ich das package in die requirements.txt aufnehmen. Es wird dann beim Start von shng automatisch installiert

                    und der Import kann im plugin einfach mittels
                    Code:
                    import pyworxcloud
                    erfolgen. (Kann man in der smarthome-details.log beim Start verfolgen)

                    Die requirements.txt sieht bei mir dann so aus :

                    Code:
                    pyworxcloud >= 1.3.0
                    Gruss Andre

                    P.S.: habe es so getestet und funktioniert, zumindest bei mir :-)

                    Kommentar


                      #11
                      Hallo Andre,

                      danke für deinen Tipp.
                      Der Grund, weshalb ich das nicht über das pip package mache ist dieser:
                      https://github.com/MTrab/pyworxcloud/issues/4

                      "meine" Version von pyworxcloud hat kein asyncio (mehr). Upstream ist das noch nicht so.
                      Oder gibt es hier eine Alternative?

                      Gruß,
                      Hendrik

                      Kommentar

                      Lädt...
                      X