Closed JanJaapKo closed 1 year ago
Insecure but I'm connected!
Please Select:20
20
/usr/lib/python3/dist-packages/urllib3/connection.py:362: SubjectAltNameWarning: Certificate for 0000-0000-0000.local has no subjectAltName
, falling back to check for a commonName
for now. This feature is being removed by major browsers and deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 for details.)
SubjectAltNameWarning
{
"protocolVersion": "2022.4.4-10"
}
Press Enter to continue...
in /ect/host: ipaddress pin.local
in tahoma_local.py: import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
And:
#self.base_url = "https://gateway-" + str(pin) + ".local:" + str(port) + "/enduser-mobile-web/1/enduserAPI"
self.base_url = "https://" + str(pin) + ".local:" + str(port) + "/enduser-mobile-web/1/enduserAPI"
Have you seen this website ?
https://somfy-developer.github.io/Somfy-TaHoma-Developer-Mode/#/
and the integration in Homeassistance
https://github.com/home-assistant/core/pull/71644/files
Maybe it helps
Yes and this one; https://www.postman.com/descent-module-geologist-46261774/workspace/somfy-api-s/collection/22623012-1ae7d722-6a6b-4427-915e-b714969a9c00
This is nicer;
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
Please Select:20 20 { "protocolVersion": "2022.4.4-10" } Press Enter to continue...
I'm still getting this error
`Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 672, in urlopen chunked=chunked, File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 376, in _make_request self._validate_conn(conn) File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 994, in _validate_conn conn.connect() File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/connection.py", line 408, in connect sslcontext=context, File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/util/ssl.py", line 370, in ssl_wrap_socket return context.wrap_socket(sock, server_hostname=server_hostname) File "/usr/lib64/python3.6/ssl.py", line 365, in wrap_socket _context=self, _session=session) File "/usr/lib64/python3.6/ssl.py", line 776, in init self.do_handshake() File "/usr/lib64/python3.6/ssl.py", line 1036, in do_handshake self._sslobj.do_handshake() File "/usr/lib64/python3.6/ssl.py", line 648, in do_handshake self._sslobj.do_handshake() ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:877)
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/requests/adapters.py", line 438, in send timeout=timeout File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/connectionpool.py", line 720, in urlopen method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2] File "/usr/lib/python3.6/site-packages/requests/packages/urllib3/util/retry.py", line 436, in increment raise MaxRetryError(_pool, url, error or ResponseError(cause)) requests.packages.urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='xxxx-xxxx-xxxx.local', port=8443): Max retries exceeded with url: /enduser-mobile-web/1/enduserAPI/apiVersion (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:877)'),))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test_tahoma.py", line 70, in
Did you edit the new params.py??
Yes and also /etc/host
It's like my first error. Maybe a compleet new clone helps?
Unfortunately. Getting the same error
Or python, ssl version different
openssl version OpenSSL 1.1.1n 15 Mar 2022
python3 --version Python 3.7.3
I'v read somewhere that ssl had issues with certificaions
My homeserver is running on Centos7, so not fully up-to-date:
openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
python3 -V
Python 3.6.8
Tried on on Debian 11 test VM
openssl version
OpenSSL 1.1.1n 15 Mar 2022
python3 --version
Python 3.9.2
Same problem
Looks like a common issue with uncertified HTTPS and cerificates
https://sectigostore.com/page/what-is-an-ssl-certificate_verify_failed-error-and-how-do-i-resolve-it/ https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error https://www.howtouselinux.com/post/ssl-certificate_verify_failed-in-python
Can we by-pass this check ?
Looks like a common issue with uncertified HTTPS and cerificates
https://sectigostore.com/page/what-is-an-ssl-certificate_verify_failed-error-and-how-do-i-resolve-it/ https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error https://www.howtouselinux.com/post/ssl-certificate_verify_failed-in-python
Can we by-pass this check ?
Is allready done by: import requests.packages.urllib3 requests.packages.urllib3.disable_warnings()
btw, i had to do pip3 install consolemenu AND console-menu
Did you try this?
pip3 install --upgrade certifi
yes
very odd, i'm on Debian 10 too
pip3 list Package Version
ansiwrap 0.8.4 asn1crypto 0.24.0 certifi 2018.8.24 chardet 3.0.4 console-menu 0.7.1 ConsoleMenu 1.0.1 cryptography 2.6.1 debugpy 1.6.3 entrypoints 0.3 httplib2 0.11.3 idna 2.6 keyring 17.1.1 keyrings.alt 3.1.1 pip 18.1 pycrypto 2.6.1 pycurl 7.43.0.2 PyGObject 3.30.4 PySimpleSOAP 1.16.2 python-apt 1.8.4.3 python-debian 0.1.35 python-debianbts 2.8.2 pyxdg 0.25 reportbug 7.5.3-deb10u1 requests 2.21.0 SecretStorage 2.3.1 setuptools 40.8.0 six 1.12.0 textwrap3 0.9.2 urllib3 1.24.1 wheel 0.32.3
when i try with my windows webbrowser to connect i'm NOT able to find the box with https://xxxx-xxxx-xxxx.local:8443/enduser-mobile-web/1/enduserAPI
When i do https://gateway-xxxx-xxxx-xxxx.local:8443/enduser-mobile-web/1/enduserAPI
I can connect with a certifcate error
Package Version
ansiwrap 0.8.4 attrs 20.2.0 babelfish 0.6.0 backoff 1.10.0 backoff-utils 1.0.1 certifi 2022.9.14 cffi 1.14.2 chardet 3.0.4 Cheetah3 3.2.5 cheroot 8.4.2 CherryPy 18.6.0 configobj 5.0.6 console-menu 0.7.1 ConsoleMenu 1.0.1 cryptography 3.1 distro 1.5.0 feedparser 5.2.1 guessit 3.3.1 idna 2.10 importlib-metadata 1.7.0 importlib-resources 3.0.0 jaraco.classes 3.1.0 jaraco.collections 3.0.0 jaraco.functools 3.0.1 jaraco.text 3.2.0 Jinja2 2.11.1 jsonschema 3.2.0 MarkupSafe 0.23 more-itertools 8.5.0 notify2 0.3.1 pip 21.3 portend 2.6 puremagic 1.11 pycparser 2.20 pyroute2 0.4.13 pyrsistent 0.16.0 PySocks 1.6.8 python-dateutil 2.8.2 pytz 2020.1 rebulk 3.0.1 requests 2.14.2 rfc3986 1.3.0 rpm 4.11.3 sabyenc3 4.0.2 setuptools 39.2.0 six 1.14.0 templated-dictionary 1.1 tempora 4.0.0 textwrap3 0.9.2 urllib3 1.25.6 validator-collection 1.4.2 zc.lockfile 2.0 zipp 3.1.0
urllib3 versions are different..
and certifi too
Mine looks newer then ab10002
Okay, i upgrade these two packages.. And yes, i'v got these errors too!
Okay, i upgrade these two packages.. And yes, i'v got these errors too!
Ok. So you could reproduce them. Not good.... but good, because i lost track on solutions
You can try this: python3 -m pip3 install urllib3==1.24.1
pip3 list
Same problem. Also tried to go back to older certifi version pip3 install certifi==2018.8.24
No success
Your python version? 3.6 is too old for domiticz; Installation Prerequisites Python version 3.7 or higher required & Domoticz version 2022.1 (due to extended plugin framework) or greater.
First, Follow the Domoticz guide on Using Python Plugins.
To install:
sudo apt-get update sudo apt-get install python3 libpython3-dev libpython3.7-dev sudo apt-get install python3-requests
Centos7 is runing on 3.6.8 and is the latest version available
As mention above i've tried on Debian11 with Python3.9 and same issue
I did a fresh install Debian 11.5, python 3.9:
pi@vm-domoticz:~/domoticz/plugins/somfy$ python3 test_tahoma.py
Traceback (most recent call last):
File "/home/pi/domoticz/plugins/somfy/test_tahoma.py", line 18, in
You need to install console-menu as well....
I did some googling and found this on installation of certificates: https://raspberrypi.stackexchange.com/questions/76419/entrusted-certificates-installation
Can you try to install the certificate metion on the page in the very first comment here?
You need to install console-menu as well....
I have allready👍 console-menu 0.7.1 ConsoleMenu 1.0.1
Same here. I've tried on Debian 11 with python 3.9.2 and on Rasberry PI with 3.7.3 Both are give the same error
python3 test_tahoma.py
Traceback (most recent call last):
File "/var/domoticz/somfy/test_tahoma.py", line 18, in <module>
mymenu = consolemenu.SelectionMenu(menuoptions)
AttributeError: module 'consolemenu' has no attribute 'SelectionMenu'
I did some googling and found this on installation of certificates: https://raspberrypi.stackexchange.com/questions/76419/entrusted-certificates-installation
Can you try to install the certificate metion on the page in the very first comment here?
I've tried it and added the entrust_l1k.crt, but same problem when trying with lynx to enter the link (console menu is not working) ;-)
lynx https://gateway-xxxx-xxxx-xxxx.local:8443/enduser-mobile-web/1/enduserAPI
I did a fresh install Debian 11.5, python 3.9: pi@vm-domoticz:~/domoticz/plugins/somfy$ python3 test_tahoma.py Traceback (most recent call last): File "/home/pi/domoticz/plugins/somfy/test_tahoma.py", line 18, in mymenu = consolemenu.SelectionMenu(menuoptions) AttributeError: module 'consolemenu' has no attribute 'SelectionMenu'
Looks like an Debian issue. I've tried a test script (https://github.com/aegirhall/console-menu) on Debian11, Raspberry Pi and Centos7 The script is working on Centos7 and not on Debian11 and PI Centos7 - python3.6.8 Debian - python 3.9.2 PI - python 3.7.3
I'm gona test with a lower debian version. New is not always good🤣
YES, Debian 11.3 is okay!
With: sudo apt install python3-venv python3-pip
Did a reinstall on debian 11.5 that's now working too!
I did some googling and found this on installation of certificates: https://raspberrypi.stackexchange.com/questions/76419/entrusted-certificates-installation
Can you try to install the certificate metion on the page in the very first comment here?
Had also a cert issue and solved
dpkg-reconfigure ca-certificates
Import certificate:
sudo mkdir /usr/share/ca-certificates/local
cd /usr/share/ca-certificates/local sudo wget https://ca.overkiz.com/overkiz-root-ca-2048.crt
sudo dpkg-reconfigure ca-certificates
select yes, local/overkiz-root-ca-2048.crt and yes
Getting local API devices: test_tahoma.py:
import time
import logging
import json
import consolemenu
#import tahoma
from tahoma_local import TahomaWebApi as tahoma
from tahoma_local import SomfyBox
import exceptions
from params import *
logging.basicConfig(format='%(asctime)s - %(levelname)-8s - %(filename)-18s - %(message)s', filename='somfy_test.log',
level=logging.DEBUG)
logging.info("=== starting test run ===")
#menuoptions = ['0 exit',"1 log in", "2 register", "3 check log in", "4 generate toke", "5 activate token", "6 get tokens", "7 delete token",
# "10 get devices", "11 get events", "12 send command",
# "20 get local API version"]
menuoptions = ['0 exit',"1 log in", "2 register", "3 check log in", "4 generate toke", "5 activate token", "6 get tokens", "7 delete token",
"10 get local devices", "11 get local events", "12 send command",
"20 get local API version"]
mymenu = consolemenu.SelectionMenu(menuoptions)
device_list = list()
tahoma = tahoma()
theBox = SomfyBox(p_pin, p_port)
if tahoma.cookie is None:
tahoma.cookie = dict(JSESSIONID='F290EEAEC03B4838EBDA4B0CD0034BAB')
if True:
while True:
for i in menuoptions:
print(i)
x = int(input("Please Select:"))
print(x)
if x == 0:
logging.info("== end test run ===")
exit()
if x == 1: #log in
status = False
try:
status = tahoma.tahoma_login(p_email, p_password)
except exceptions.LoginFailure as exp:
print("Failed to login: " + str(exp))
if tahoma.cookie is None:
tahoma.cookie = dict(JSESSIONID='F290EEAEC03B4838EBDA4B0CD0034BAB')
print("login status: "+str(status))
if x == 2: # registyer listener
tahoma.register_listener()
if tahoma.listenerId is None:
tahoma.listenerId = 'b4e62511-ac10-3e01-60e0-9b9f656aea77'
if x == 3: print(str(tahoma.logged_in)) #check log in
if x == 4: #generate token
response = tahoma.generate_token(p_pin)
print(json.dumps(response, sort_keys = True, indent=4))
if x == 5: #activate token
response = tahoma.activate_token(p_pin)
print(json.dumps(response, sort_keys = True, indent=4))
if x == 6: #get list of tokens
response = tahoma.get_tokens(p_pin)
print(json.dumps(response, sort_keys = True, indent=4))
if x == 7:#delete token
uuid = input("Please enter uuid to delete:")
response = tahoma.delete_tokens(p_pin, uuid)
print(json.dumps(response, sort_keys = True, indent=4))
#if x == 10: print(tahoma.get_devices(device_list))
if x == 10: #get devices of local API
response = theBox.get_devices()
print(json.dumps(response, sort_keys = True, indent=4))
input("Press Enter to continue...")
#if x == 11: print(tahoma.get_events())
if x == 11: #get events of local API
response = theBox.get_events()
print(json.dumps(response, sort_keys = True, indent=4))
input("Press Enter to continue...")
if x == 12:
data = '{"actions": [{"commands": [{"name": "open"}], "deviceURL": "io://1237-2024-7920/10464619"}], "label": "Domoticz - Somfy - Kamer_Klein - open"}'
print(tahoma.tahoma_command(json.dumps(data)))
if x == 20: #get version of local API
response = theBox.get_version()
print(json.dumps(response, sort_keys = True, indent=4))
input("Press Enter to continue...")
# except (ValueError) as err:
# print("error in menu keuze")
else:
logging.error("initialisation failed")
tahoma_local.py:
from lib2to3.pgen2 import tokenize
import requests
import logging
import exceptions
import urllib.parse
import datetime
import time
import json
try:
import DomoticzEx as Domoticz
except ImportError:
import fakeDomoticz as Domoticz
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
class TahomaWebApi:
def __init__(self):
self.base_url = "https://ha101-1.overkiz.com"
self.headers_url = {"Content-Type": "application/x-www-form-urlencoded"}
self.headers_json = {"Content-Type": "application/json"}
self.login_url = "/enduser-mobile-web/enduserAPI/login"
self.timeout = 10
self.__expiry_date = datetime.datetime.now()
self.logged_in_expiry_days = 6
self.cookie = None
self.token = None
self.__logged_in = False
@property
def logged_in(self):
logging.debug("checking logged in status: self.__logged_in = "+str(self.__logged_in)+" and self.__expiry_date >= datetime.datetime.now() = " + str(self.__expiry_date >= datetime.datetime.now()))
if self.__logged_in and (self.__expiry_date >= datetime.datetime.now()):
return True
else:
return False
def tahoma_login(self, username, password):
data = {"userId": username, "userPassword": password}
response = requests.post(self.base_url + self.login_url, headers=self.headers_url, data=data, timeout=self.timeout)
Data = response.json()
logging.debug("Login respone: status_code: '"+str(response.status_code)+"' reponse body: '"+str(Data)+"'")
if (response.status_code == 200 and not self.__logged_in):
self.__logged_in = True
self.__expiry_date = datetime.datetime.now() + datetime.timedelta(days=self.logged_in_expiry_days)
logging.info("Tahoma authentication succeeded, login valid until " + self.__expiry_date.strftime("%Y-%m-%d %H:%M:%S"))
#self.cookie = response.cookies
self.cookie = response.cookies
logging.debug("login: cookies: '"+ str(response.cookies)+"', headers: '"+str(response.headers)+"'")
#self.register_listener()
elif ((response.status_code == 401) or (response.status_code == 400)):
strData = Data["error"]
#logging.error("Tahoma error: must reconnect")
self.__logged_in = False
self.cookie = None
self.listenerId = None
if ("Too many" in strData):
logging.error("Too many connections, must wait")
#self.heartbeat = True
raise exceptions.LoginFailure("Too many connections, must wait")
elif ("Bad credentials" in strData):
logging.error("login failed: Bad credentials, please update credentials and restart plugin")
#self.heartbeat = False
raise exceptions.LoginFailure("Bad credentials, please update credentials and restart plugin")
else:
logging.error("login failed, unhandled reason: "+strData)
raise exceptions.LoginFailure("login failed, unhandled reason: "+strData)
if (not self.__logged_in):
self.tahoma_login(username, password)
return
return self.__logged_in
def generate_token(self, pin):
url_gen = "/enduser-mobile-web/enduserAPI/config/"+pin+"/local/tokens/generate"
logging.debug("generate token: url_gen = '" + url_gen + "'")
logging.debug("generate token: cookie = '" + str(self.cookie) + "'")
response = requests.get(self.base_url + url_gen, headers=self.headers_json, cookies=self.cookie)
if response.status_code == 200:
self.token = response.json()['token']
logging.debug("succeeded to activate token: " + str(self.token))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to generate token")
raise exceptions.LoginFailure("failed to generate token")
return response.json()
def activate_token(self, pin):
url_act = "/enduser-mobile-web/enduserAPI/config/"+pin+"/local/tokens"
data_act = {"label": "Domoticz token", "token": self.token, "scope": "devmode"}
response = requests.post(self.base_url + url_act, headers=self.headers_json, json=data_act, cookies=self.cookie)
if response.status_code == 200:
#self.token = response.json()['token']
logging.debug("succeeded to activate token: " + str(self.token))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to activate token")
raise exceptions.LoginFailure("failed to activate token")
return response.json()
def get_tokens(self, pin):
url_act = "/enduser-mobile-web/enduserAPI/config/"+pin+"/local/tokens/devmode"
response = requests.get(self.base_url + url_act, headers=self.headers_json, cookies=self.cookie)
if response.status_code == 200:
#self.token = response.json()['token']
logging.debug("succeeded to get tokens: " + str(response.json()))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to get tokens")
raise exceptions.LoginFailure("failed to get tokens")
return response.json()
def delete_tokens(self, pin, uuid):
url_del = "/enduser-mobile-web/enduserAPI/config/"+pin+"/local/tokens/"+str(uuid)
response = requests.delete(self.base_url + url_del, headers=self.headers_json, cookies=self.cookie)
if response.status_code == 200:
logging.debug("succeeded to delete token: " + str(response.json()))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to delete token")
raise exceptions.LoginFailure("failed to delete tokens")
return response.json()
class SomfyBox:
def __init__(self, pin, port):
self.base_url = "https://" + str(pin) + ".local:" + str(port) + "/enduser-mobile-web/1/enduserAPI"
#self.headers_json = {"Content-Type": "application/json", "Authorization": "Bearer "}
self.headers_json = {"Content-Type": "application/json", "Authorization": "Bearer 1234567890xxxxxxxxxx"} # put here generated 20char token
def set_token(self, token):
self.headers_json["Authorization"] = "Bearer " + str(token)
def get_version(self):
response = requests.get(self.base_url + "/apiVersion", headers=self.headers_json)
if response.status_code == 200:
logging.debug("succeeded to get API version: " + str(response.json()))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to get API version")
raise exceptions.LoginFailure("failed to get API version")
return response.json()
def get_devices(self):
#response = requests.get(self.base_url + "/apiVersion", headers=self.headers_json)
response = requests.get(self.base_url + "/setup/devices", headers=self.headers_json)
print (response)
if response.status_code == 200:
logging.debug("succeeded to get local API devices: " + str(response.json()))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to get local API devices")
raise exceptions.LoginFailure("failed to get local API devices")
return response.json()
def get_events(self):
#response = requests.get(self.base_url + "/apiVersion", headers=self.headers_json)
response = requests.get(self.base_url + "/events/<uuid>fetch", headers=self.headers_json)
print (response)
if response.status_code == 200:
logging.debug("succeeded to get local API events: " + str(response.json()))
elif ((response.status_code == 401) or (response.status_code == 400)):
self.__logged_in = False
self.cookie = None
logging.error("failed to get local API events")
raise exceptions.LoginFailure("failed to get local API events")
return response.json()
def new_method(self):
self.set_token()
@ab10002 does this get_events method work? That endpoint you're using is not documented....
I've pushed a new version with at least the get_devices in. I will move it into another branch now to restructure the existing bit such that it will get the same interface towards Domoticz as the new local interface. This will make it easier to maintain. Will provide instructions how to pull later
@ab10002 does this get_events method work? That endpoint you're using is not documented....
I've pushed a new version with at least the get_devices in. I will move it into another branch now to restructure the existing bit such that it will get the same interface towards Domoticz as the new local interface. This will make it easier to maintain. Will provide instructions how to pull later
No, getting the right uuid is a problem
OK get it, that should be the listener ID. Just pushed new version. Be sure to do 23 before 24 (otherwise a nice error will be thrown)
Please Select:23
23
Traceback (most recent call last):
File "/home/pi/somfy/test_tahoma.py", line 79, in
2022-09-26 20:28:57,198 - INFO - test_tahoma.py - === starting test run ===
2022-09-26 20:29:04,696 - DEBUG - connectionpool.py - Starting new HTTPS connection (1): 0000-0000-0000.local:8443
2022-09-26 20:29:04,827 - DEBUG - connectionpool.py - https://0000-0000-0000.local:8443 "POST /enduser-mobile-web/1/enduserAPI/events/register HTTP/1.1" 401 77
2022-09-26 20:29:04,828 - DEBUG - tahoma_local.py - <Response [401]>
2022-09-26 20:29:04,828 - ERROR - tahoma_local.py - failed to get local API events
Maybe this is missing for the local API?? (except for getting version) --header 'Authorization: Bearer 62ecd0f3cdda9a49c2c1'
401 is indeed not authorised error. Did you do the login steps first to get the token?
Yes but faik is that not nessecary for local
You always need a token in the header (it is the Bearer token). I'll figure something to re-use existing tokens, that is (at least during test) a bit more efficient. For now, I'll throw an error on no token.
So for now you need steps: 1, 4, 5, and then any of the 20's
The Somfy box offers also a local API, which will replace at some point the web based API. With this improvement we will add support from the plugin to the local API.
API description: https://github.com/Somfy-Developer/Somfy-TaHoma-Developer-Mode