Neues Python FHEM API-Modul auf PyPI

Begonnen von domschl, 01 Januar 2017, 12:15:41

Vorheriges Thema - Nächstes Thema

TK67

Danke für die neue Version, das mit den ähnlichen Readings-Namen funktioniert jetzt ohne Probleme.
Eine Telnet Verbindung wirft mir jetzt allerdings bei jeder Abfrage eine Exception, der Wert wird aber ausgelesen.

Testabfrage : print fh.get_device_reading("test", "state")
Ausgabe : {u'Value': 1, u'Time': datetime.datetime(2019, 8, 19, 1, 46, 48)}

Debug-Log:
Aug 19 01:46:47 Sending: jsonlist2 NAME~test
Aug 19 01:46:47 Connected, sending...
Aug 19 01:46:47 Sent msg, len=20
Aug 19 01:46:47 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 01:46:47 JSON answer received.


Wäre nett wenn man da nochmal drüber schaut.

Gruß TK67
FHEM auf Raspberry Pi 3 Model B+

domschl

"Resource temporary unavailable" ist mit hoher Wahrscheinlichkeit einfach ein Timing-Problem:
Der Python-client schickt eine Anfrage, und erwartet dann "direkt" eine Antwort. Wenn der Server
eine etwas "gemütlichere" hardware verwendet, dann ist die Antwort nicht sofort verfügbar, was
eine "Resource temporary unavailable" exception erzeugt. Der client wiederholt dann die lese-
Anfrage und dann kommt die Antwort auch an.

Die Ausgabe mit der Exception ist also im Prinzip einfach überflüssig, da sich alles korrekt ver-
hält. Ich werde das beim nächsten update ändern, so daß nur bei wirklichen, permanenten
Timeouts ein Fehler kommt.

Bis dahin: Fehlermeldung einfach ignorieren. Was für einen FHEM-Server setzt Du ein? Ist
die Annahme mit Timing plausibel? Ich kann den Fehler nämlich nicht nachstellen, und auch
der automatische Test mit TravisCI zeigt keine Probleme.

Bug-Tracker: https://github.com/domschl/python-fhem/issues/18

Details zu ähnlichem Problem: https://stackoverflow.com/questions/39145357/python-error-socket-error-errno-11-resource-temporarily-unavailable-when-s

TK67

Bei mir läuft Fhem mit allen Updates auf einem Raspberry Pi 3 b+

Habe jetzt mal ein frisches System auf meinem Raspberry Pi 2 Testrechner mit Fhem aufgesetzt.
Dort zeigt sich auch das Timing Problem.

cat /etc/os-release:
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"


Obwohl dort weiter nichts läuft.
Top:
top - 12:51:45 up 56 min,  2 users,  load average: 0.00, 0.00, 0.00
Tasks: 111 total,   1 running, 110 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.2 us,  0.2 sy,  0.0 ni, 99.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :    926.1 total,    716.6 free,     72.7 used,    136.8 buff/cache
MiB Swap:    100.0 total,    100.0 free,      0.0 used.    795.6 avail Mem


Fehler ist bei mir auch reproduzierbar, hier mal eine Schleife mit time sleep von 5 Sekunden.
Aug 19 12:53:26 Sending: jsonlist2 NAME~test
Aug 19 12:53:26 Connected, sending...
Aug 19 12:53:26 Sent msg, len=20
Aug 19 12:53:26 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 12:53:26 JSON answer received.
Aug 19 12:53:31 ------------
Aug 19 12:53:31 Sending: jsonlist2 NAME~test
Aug 19 12:53:31 Connected, sending...
Aug 19 12:53:31 Sent msg, len=20
Aug 19 12:53:31 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 12:53:31 JSON answer received.
Aug 19 12:53:36 ------------
Aug 19 12:53:36 Sending: jsonlist2 NAME~test
Aug 19 12:53:36 Connected, sending...
Aug 19 12:53:36 Sent msg, len=20
Aug 19 12:53:36 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 12:53:36 JSON answer received.
Aug 19 12:53:41 ------------
Aug 19 12:53:41 Sending: jsonlist2 NAME~test
Aug 19 12:53:41 Connected, sending...
Aug 19 12:53:41 Sent msg, len=20
Aug 19 12:53:42 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 12:53:42 JSON answer received.
Aug 19 12:53:47 ------------
Aug 19 12:53:47 Sending: jsonlist2 NAME~test
Aug 19 12:53:47 Connected, sending...
Aug 19 12:53:47 Sent msg, len=20
Aug 19 12:53:47 Exception in non-blocking. Error: [Errno 11] Resource temporarily unavailable
Aug 19 12:53:47 JSON answer received.
FHEM auf Raspberry Pi 3 Model B+

domschl

Das ist schon verrückt. Ich habe noch einen alten Raspi-1 mit Fhem im Netz, und habe damit getestet.
Sowohl mit Py2 als auch Py3 kein Problem.

Ich vermute, daß der Unterschied dann im Netzwerk timing liegt.

Aber kein Problem, ich weiß wie ich das korrigieren kann. Kommt mit dem nächsten Release.

ch.eick

Hallo zusammen.

Ich bin reiner Python Anwender und versuche die python-fhem Kopplung zu nutzen

Als user fhem habe ich folgendes gemacht

$ pip install fhem
Collecting fhem
  Downloading https://www.piwheels.org/simple/fhem/fhem-0.6.2-py2.py3-none-any.whl
Installing collected packages: fhem
Successfully installed fhem-0.6.2


Der erste Test des python Skripts ohne das interface

import json
import asyncio
from vallox_websocket_api import Client

client = Client('<IP-Adresse Quelle>')
async def run():
    metrics = await client.fetch_metrics([
      'A_CYC_TEMP_EXHAUST_AIR',
      'A_CYC_TEMP_EXTRACT_AIR',
      'A_CYC_TEMP_OUTDOOR_AIR',
      'A_CYC_TEMP_SUPPLY_AIR',
      'A_CYC_TEMP_SUPPLY_CELL_AIR'
    ])

    from pprint import pprint
    pprint(json.dumps(metrics))

asyncio.get_event_loop().run_until_complete(run())


Output in der Shell

$ python3.5 ./test_ce_status_temp.py
('{"A_CYC_TEMP_SUPPLY_CELL_AIR": 23.1, "A_CYC_TEMP_EXTRACT_AIR": 24.1, '
'"A_CYC_TEMP_EXHAUST_AIR": 24.4, "A_CYC_TEMP_SUPPLY_AIR": 24.6, '
'"A_CYC_TEMP_OUTDOOR_AIR": 23.5}')



Nun der Versuch mit Interface

import json
import asyncio
from vallox_websocket_api import Client
import fhem

client = Client('<IP-Adresse Quelle>')
async def run():
    metrics = await client.fetch_metrics([
      'A_CYC_TEMP_EXHAUST_AIR',
      'A_CYC_TEMP_EXTRACT_AIR',
      'A_CYC_TEMP_OUTDOOR_AIR',
      'A_CYC_TEMP_SUPPLY_AIR',
      'A_CYC_TEMP_SUPPLY_CELL_AIR'
    ])

    from pprint import pprint
    pprint(json.dumps(metrics))

    fh = fhem.Fhem("<IP-Adresse Ziel>", protocol="http", port=8083)

    fh.send_cmd("setreading KWL output ", json.dumps(metrics))

asyncio.get_event_loop().run_until_complete(run())



$ python3.5 ./test_ce_status_temp.py
Traceback (most recent call last):
  File "./test_ce_status_temp.py", line 1, in <module>
    import fhem
ImportError: No module named 'fhem'


Die vorherige Installation von vallox_websocket_api wurde ebenfalls mit "pip install vallox_websocket_api" durchgeführt und das Modul wird gefunden. Wo liegt nun der Fehler beim fhem Modul?

Achtung der Name fhem als Modulname ist wirklich sehr unglücklich!!!

Gruß
    Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

domschl

Meine Vermutung ist hier, daß auf dem System noch python2 das Standard-Python ist, und daß die PIP Installation das Python-2 PIP verwendet hat, und somit fhem nur unter Python2 zur Verfügung steht. Kann mit:


pip --version


Überprüft werden. Falls das wirklich die Ursache ist, einfach die Py3 Version mit


pip3 install -U fhem


installieren. Zusätzlich kann das Modul einfach im REPL-Mode von Python getestet werden. Einfach Python3 ohne parameter aufrufen, und dann in der REPL einfach mal `import fhem` eingeben.

ch.eick

Okay,
eine eigene Teilantwort. Es muss natürlich in das richtige Python installiert werden, in meinem Fall python3.5


$ python3.5 -m pip install fhem
Collecting fhem
  Using cached https://www.piwheels.org/simple/fhem/fhem-0.6.2-py2.py3-none-any.whl
Installing collected packages: fhem
Successfully installed fhem-0.6.2

Der Rest wird wohl ein login Problem sein....ich schau mal ins fhem log

$ python3.5 ./test_ce_status_temp.py
('{"A_CYC_TEMP_SUPPLY_AIR": 24.3, "A_CYC_TEMP_EXTRACT_AIR": 24.1, '
'"A_CYC_TEMP_EXHAUST_AIR": 24.5, "A_CYC_TEMP_OUTDOOR_AIR": 23.2, '
'"A_CYC_TEMP_SUPPLY_CELL_AIR": 23.0}')
Failed to send msg, len=0, timed out
No valid answer on send when expecting csrf.
Failed to send msg, len=0, HTTP Error 401: Authorization Required
No valid answer on send when expecting csrf.
CSRF token not available!
Traceback (most recent call last):
  File "./test_ce_status_temp.py", line 23, in <module>
    asyncio.get_event_loop().run_until_complete(run())
  File "/usr/lib/python3.5/asyncio/base_events.py", line 466, in run_until_complete
    return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 293, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "./test_ce_status_temp.py", line 21, in run
    fh.send_cmd("setreading KWL output ", json.dumps(metrics))
  File "/home/fhem/.local/lib/python3.5/site-packages/fhem/__init__.py", line 315, in send_cmd
    return self.send(msg, timeout=timeout)
  File "/home/fhem/.local/lib/python3.5/site-packages/fhem/__init__.py", line 277, in send
    ans = urlopen(ccmd, paramdata, timeout=timeout)
  File "/usr/lib/python3.5/urllib/request.py", line 163, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib/python3.5/urllib/request.py", line 466, in open
    response = self._open(req, data)
  File "/usr/lib/python3.5/urllib/request.py", line 484, in _open
    '_open', req)
  File "/usr/lib/python3.5/urllib/request.py", line 444, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.5/urllib/request.py", line 1282, in http_open
    return self.do_open(http.client.HTTPConnection, req)
  File "/usr/lib/python3.5/urllib/request.py", line 1254, in do_open
    h.request(req.get_method(), req.selector, req.data, headers)
  File "/usr/lib/python3.5/http/client.py", line 1107, in request
    self._send_request(method, url, body, headers)
  File "/usr/lib/python3.5/http/client.py", line 1152, in _send_request
    self.endheaders(body)
  File "/usr/lib/python3.5/http/client.py", line 1103, in endheaders
    self._send_output(message_body)
  File "/usr/lib/python3.5/http/client.py", line 934, in _send_output
    self.send(msg)
  File "/usr/lib/python3.5/http/client.py", line 877, in send
    self.connect()
  File "/usr/lib/python3.5/http/client.py", line 849, in connect
    (self.host,self.port), self.timeout, self.source_address)
  File "/usr/lib/python3.5/socket.py", line 700, in create_connection
    sock.settimeout(timeout)
TypeError: an integer is required (got type str)
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

Zitat von: domschl am 21 August 2019, 16:31:58
Meine Vermutung ist hier, daß auf dem System noch python2 das Standard-Python ist, und daß die PIP Installation das Python-2 PIP verwendet hat, und somit fhem nur unter Python2 zur Verfügung steht. Kann mit:


pip --version


Überprüft werden. Falls das wirklich die Ursache ist, einfach die Py3 Version mit


pip3 install -U fhem


installieren. Zusätzlich kann das Modul einfach im REPL-Mode von Python getestet werden. Einfach Python3 ohne parameter aufrufen, und dann in der REPL einfach mal `import fhem` eingeben.

Sorry, das hat sich überschnitten.

Ich benutze Python heute das erste Mal. ;D
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

Hallo nochmal.

Ich habe dann erst noch fhem auf https umkonfiguriert...
Nun klappt die https Kopplung mit Userid und Passwort auch über das Python-fhem interface.

Und hier mein lauffähiges Skript

# import logging
# logging.basicConfig(level=logging.DEBUG)

import fhem
import json
import asyncio
from vallox_websocket_api import Client

import sys

kwl = sys.argv[1]
web = sys.argv[2]
usr = sys.argv[3]
pwd = sys.argv[4]


client = Client(kwl)
async def run():
    metrics = await client.fetch_metrics([
      'A_CYC_TEMP_EXHAUST_AIR',
      'A_CYC_TEMP_EXTRACT_AIR',
      'A_CYC_TEMP_OUTDOOR_AIR',
      'A_CYC_TEMP_SUPPLY_AIR',
      'A_CYC_TEMP_SUPPLY_CELL_AIR'
    ])

    message = json.dumps(metrics)

    from pprint import pprint
    pprint(message)

    fh = fhem.Fhem("" + web + "", protocol="https", port=8083, username="" + usr + "" , password="" + pwd + "")

    fh.send_cmd("setreading KWL output " + message)

asyncio.get_event_loop().run_until_complete(run())


Das fhem define, was die Daten sammelt und direkt mit expandJSON in einzelne readings aufteilt

defmod KWL expandJSON KWL:output:.\{.*}


Und ein Bild im Anhang

Viele Grüße
    Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

#39
Jetzt noch eine Frage, um das unschöne speichern von Benutzerkennung und Passwort zu umgehen.

Ist es möglich, wie bei einer ssl Verbindung, mit einem hinterlegten Key, auch ohne Passwort die Verbindung zu nutzen?
Das mit dem cafile habe ich nicht verstanden ;-)

Gruß
   Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

domschl

Wenn man auf dem FHEM-Server einen WEB-Zugang mit HTTPS ohne Passwort-Authentifizierung anlegt, dann kann das Python Modul diese Verbindung verwenden. Einfach dann kein username/password angeben.
Die Verbindung ist dann SSL-Encrypted, nutzt aber keine Benutzer-Authentifizierung.

Was der FHEM-Server meines Wissens nicht kann, ist der Zugang per public/private Key. Dazu müßte man dann wohl von VPN oder SSH ausweichen.

Alternativ kann man noch im Python-Script die Credentials von einer externen Datei einlesen. Damit sind User/Pwd zumindestens nicht mehr hart kodiert im Script. So macht es z.b. das Datenbank-Modul von FHEM für username/pwd vom externen SQL Server, die credentials liegen in einem externen File db.conf.

ch.eick

#41
Zitat von: domschl am 21 August 2019, 18:14:11
Wenn man auf dem FHEM-Server einen WEB-Zugang mit HTTPS ohne Passwort-Authentifizierung anlegt, dann kann das Python Modul diese Verbindung verwenden. Einfach dann kein username/password angeben.
Die Verbindung ist dann SSL-Encrypted, nutzt aber keine Benutzer-Authentifizierung.

Was der FHEM-Server meines Wissens nicht kann, ist der Zugang per public/private Key. Dazu müßte man dann wohl von VPN oder SSH ausweichen.

Alternativ kann man noch im Python-Script die Credentials von einer externen Datei einlesen. Damit sind User/Pwd zumindestens nicht mehr hart kodiert im Script. So macht es z.b. das Datenbank-Modul von FHEM für username/pwd vom externen SQL Server, die credentials liegen in einem externen File db.conf.

Hmmm, was ist das sicherste?

1) HTTPS ohne Passwort könnte jeder aufrufen, der ins netzt kommt.

2) Die Passwort Datei kann man lesen, wenn man auf dem System ist. Mit "chmod 600" nur der Eigentümer fhem.

3) Mit Ablage im fhem device und Skriptaufruf mit Parameter, wie in meinem Beispiel, kann jeder dran der fhem Zugang hat.

Was wäre Deine Empfehlung?

Ich kann noch kein Python. Hast Du ein Beispiel für die Dateivariante?

Viele Grüße
    Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

domschl

Ich denke, da ja wohl das script lokal auf dem FHEM server laufen wird, ist vermutlich die beste Idee, kein zusätzliches "Loch" ins FHEM zu bohren, und die user/pwd aus einer Datei zu lesen.

Am einfachsten speichert man die Datei als JSON-File:

pwd.json:

{
    "username": "ich",
    "password": "secretsauce"
}


Im Python kann man das lesen mit:

import json

try:
    with open('pwd.json', 'r') as f:
        credentials=json.load(f)
except Exception as e:
    print('Something went wrong: {}'.format(e))

print("User: {}".format(credentials["username"]))
print("Pwd: {}".format(credentials["password"]))


credentials ist dann ein Python-dictionary. Der username ist in credentials["username"] und das Passwort in credentials["password']

ch.eick

Super, es läuft  8)


# import logging
# logging.basicConfig(level=logging.DEBUG)

import fhem
import json
import asyncio
from vallox_websocket_api import Client

import sys
kwl = sys.argv[1]
web = sys.argv[2]

try:
    with open('pwd.json', 'r') as f:
        credentials=json.load(f)
except Exception as e:
    print('Something went wrong: {}'.format(e))

client = Client(kwl)
async def run():
    metrics = await client.fetch_metrics([
      'A_CYC_TEMP_EXHAUST_AIR',
      'A_CYC_TEMP_EXTRACT_AIR',
      'A_CYC_TEMP_OUTDOOR_AIR',
      'A_CYC_TEMP_SUPPLY_AIR',
      'A_CYC_TEMP_SUPPLY_CELL_AIR'
    ])

    message = json.dumps(metrics)

    from pprint import pprint
    pprint(message)

    fh = fhem.Fhem("" + web + "", protocol="https", port=8083, username="" + format(credentials["username"]) + "" , password="" + format(credentials["password"]) + "")

    fh.send_cmd("setreading KWL output " + message)

asyncio.get_event_loop().run_until_complete(run())


Die pwd.json Datei ist mit 600nur für den Owner fhem freigegeben.

Und es ist kein Loch gebohrt  ;)

Vielen Dank
    Christian

P.S. Ich hoffe ich war nicht zu schwierig.
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

Wäre es nicht sinnvoll, bei dem major change, wenn das Python 2 raus fliegt dann auch das Modul im Python umzubenennen?
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick