Skip to content
This repository has been archived by the owner on Feb 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #188 from danielperna84/devel
Browse files Browse the repository at this point in the history
0.1.53
  • Loading branch information
danielperna84 authored Dec 5, 2018
2 parents 081b999 + de78977 commit f1482ff
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 19 deletions.
10 changes: 10 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Version 0.1.53 (2018-12-05)
- Changed loglevel for getValue to `warning` (Issue #153) @danielperna84
- Channel for LOW_BAT always is 0 for HmIP devices (Issue #186) @danielperna84
- Support hostnames for XML-RPC endpoints @danielperna84
- Add support for HmIP-eTRV-B @SiwYv
- Added support for HmIP-FDT @DieterChmod
- Added support for HB-UW-Sen-THPL-O and HB-UW-Sen-THPL-I @rgriebl
- Add support for SSL and authentication with CCU3 @danielperna84
- Added support for HmIP-B1 @jberrenberg

Version 0.1.52 (2018-11-15)
- Added HmIP-MOD-TM (Issue #170) @danielperna84
- Added HM-LC-RGBW-WM @chr1st1ank
Expand Down
66 changes: 57 additions & 9 deletions pyhomematic/_hm.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
import threading
import json
import ssl
import urllib.request
import urllib.parse
import xml.etree.ElementTree as ET
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler
Expand Down Expand Up @@ -45,6 +47,38 @@
working = False


def make_http_credentials(username=None, password=None):
"""Build auth part for api_url."""
credentials = ''
if username is None:
return credentials
if username is not None:
if ':' in username:
return credentials
credentials += username
if credentials and password is not None:
credentials += ":%s" % password
return "%s@" % credentials


def build_api_url(host=REMOTES['default']['ip'],
port=REMOTES['default']['port'],
path=REMOTES['default']['port'],
username=None,
password=None,
ssl=False):
"""Build API URL from components."""
credentials = make_http_credentials(username, password)
scheme = 'http'
if not path:
path = ''
if path and not path.startswith('/'):
path = "/%s" % path
if ssl:
scheme += 's'
return "%s://%s%s:%i%s" % (scheme, credentials, host, port, path)


# Object holding the methods the XML-RPC server should provide.
class RPCFunctions():

Expand Down Expand Up @@ -399,10 +433,15 @@ def __init__(self, *args, **kwargs):
self._skipinit = kwargs.pop("skipinit", False)
self._callbackip = kwargs.pop("callbackip", None)
self._callbackport = kwargs.pop("callbackport", None)
self._ssl = kwargs.pop("ssl", False)
self._verify_ssl = kwargs.pop("verify_ssl", True)
self.lock = threading.Lock()
if self._ssl and not self._verify_ssl and self._verify_ssl is not None:
kwargs['context'] = ssl._create_unverified_context()
xmlrpc.client.ServerProxy.__init__(self, *args, **kwargs)
self._remoteip, self._remoteport = self._ServerProxy__host.split(':')
self._remoteport = int(self._remoteport)
urlcomponents = urllib.parse.urlparse(args[0])
self._remoteip = urlcomponents.hostname
self._remoteport = urlcomponents.port
LOG.debug("LockingServerProxy.__init__: Getting local ip")
tmpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tmpsocket.connect((self._remoteip, self._remoteport))
Expand Down Expand Up @@ -469,20 +508,29 @@ def __init__(self,
for remote, host in self.remotes.items():
# Initialize XML-RPC
try:
socket.inet_pton(socket.AF_INET, host['ip'])
socket.gethostbyname(host['ip'])
except Exception as err:
LOG.warning("Skipping proxy: %s" % str(err))
continue
if 'path' not in host:
host['path'] = ""
LOG.info("Creating proxy %s. Connecting to http://%s:%i%s" %
host['path'] = ''
LOG.info("Creating proxy %s. Connecting to %s:%i%s" %
(remote, host['ip'], host['port'], host['path']))
host['id'] = "%s-%s" % (self._interface_id, remote)
try:
self.proxies[host['id']] = LockingServerProxy("http://%s:%i%s" % (host['ip'], host['port'], host['path']),
callbackip=host.get('callbackip', None),
callbackport=host.get('callbackport', None),
skipinit=not host.get('connect', True))
api_url = build_api_url(host=host['ip'],
port=host['port'],
path=host['path'],
username=host.get('username'),
password=host.get('password'),
ssl=host.get('ssl'))
self.proxies[host['id']] = LockingServerProxy(
api_url,
callbackip=host.get('callbackip', None),
callbackport=host.get('callbackport', None),
skipinit=not host.get('connect', True),
ssl=host.get('ssl', False),
verify_ssl=host.get('verify_ssl', True))
except Exception as err:
LOG.warning("Failed connecting to proxy at http://%s:%i%s" %
(host['ip'], host['port'], host['path']))
Expand Down
6 changes: 4 additions & 2 deletions pyhomematic/devicetypes/actors.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class HMActor(HMDevice):
"""
Generic HM Actor Object
"""
pass


class GenericBlind(HMActor, HelperActorLevel):
Expand Down Expand Up @@ -130,7 +129,9 @@ class IPDimmer(GenericDimmer):
"""
@property
def ELEMENT(self):
return [3]
if "PDT" in self._TYPE:
return [3]
return [2]


class IPKeyDimmer(GenericDimmer, HelperWorking, HelperActionPress):
Expand Down Expand Up @@ -665,6 +666,7 @@ def turn_off_effect(self):
"HmIP-BSM": IPKeySwitchPowermeter,
"HMIP-BDT": IPKeyDimmer,
"HmIP-BDT": IPKeyDimmer,
"HmIP-FDT": IPDimmer,
"HmIP-PDT": IPDimmer,
"HM-Sec-Key": KeyMatic,
"HM-Sec-Key-S": KeyMatic,
Expand Down
4 changes: 2 additions & 2 deletions pyhomematic/devicetypes/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ def getValue(self, key):
self._VALUES[key] = returnvalue
return returnvalue
except Exception as err:
LOG.error("HMGeneric.getValue: %s on %s Exception: %s", key,
self._ADDRESS, err)
LOG.warning("HMGeneric.getValue: %s on %s Exception: %s", key,
self._ADDRESS, err)
return False


Expand Down
2 changes: 1 addition & 1 deletion pyhomematic/devicetypes/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self, device_description, proxy, resolveparamsets=False):
super().__init__(device_description, proxy, resolveparamsets)

# init metadata
self.ATTRIBUTENODE.update({"LOW_BAT": self.ELEMENT})
self.ATTRIBUTENODE.update({"LOW_BAT": [0]})

def low_batt(self, channel=None):
""" Returns if the battery is low. """
Expand Down
21 changes: 19 additions & 2 deletions pyhomematic/devicetypes/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def ELEMENT(self):

class ShutterContact(IPShutterContact, HelperSabotage, HelperRssiPeer):
"""Door / Window contact that emits its open/closed state."""
pass


class MaxShutterContact(HMBinarySensor, HelperBinaryState, HelperLowBat):
Expand Down Expand Up @@ -282,7 +281,6 @@ def ELEMENT(self):

class MotionV2(Motion, HelperSabotage):
"""Motion detection version 2."""
pass


class MotionIP(HMBinarySensor, HMSensor):
Expand Down Expand Up @@ -726,6 +724,23 @@ def __init__(self, device_description, proxy, resolveparamsets=False):
self.ATTRIBUTENODE.update({"OPERATING_VOLTAGE": [0]})


class UniversalSensor(WeatherStation, HelperLowBat, HelperRssiPeer):
"""Universal sensor. (https://wiki.fhem.de/wiki/Universalsensor)"""

def __init__(self, device_description, proxy, resolveparamsets=False):
super().__init__(device_description, proxy, resolveparamsets)

# init metadata
self.SENSORNODE.update({"BatteryVoltage": [1],
"LUMINOSITY": [1]})

def get_luminosity(self, channel=None):
return float(self.getSensorData("LUMINOSITY", channel))

def get_battery_voltage(self, channel=None):
return float(self.getSensorData("BatteryVoltage", channel))


DEVICETYPES = {
"HM-Sec-SC": ShutterContact,
"HM-Sec-SC-2": ShutterContact,
Expand Down Expand Up @@ -811,4 +826,6 @@ def __init__(self, device_description, proxy, resolveparamsets=False):
"HmIP-SPDR": IPPassageSensor,
"IT-Old-Remote-1-Channel": SmartwareMotion,
"HmIP-SLO": IPBrightnessSensor,
"HB-UW-Sen-THPL-O": UniversalSensor,
"HB-UW-Sen-THPL-I": UniversalSensor,
}
2 changes: 2 additions & 0 deletions pyhomematic/devicetypes/thermostats.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ def turnoff(self):
"HMIP-eTRV": IPThermostat,
"HmIP-eTRV": IPThermostat,
"HmIP-eTRV-2": IPThermostat,
"HmIP-eTRV-B": IPThermostat,
"HmIP-eTRV-B1": IPThermostat,
"HmIP-STHD": IPThermostatWall,
"HmIP-STH": IPThermostatWall,
"HmIP-WTH-2": IPThermostatWall,
Expand Down
2 changes: 0 additions & 2 deletions pyhomematic/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ class HMException(Exception):
"""
Base exception for all exceptions pyhomeamtic will raise.
"""
pass


class HMRpcException(HMException):
"""
Exception for errors while communicating via XML-RPC.
"""
pass
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def readme():

PACKAGE_NAME = 'pyhomematic'
HERE = os.path.abspath(os.path.dirname(__file__))
VERSION = '0.1.52'
VERSION = '0.1.53'

PACKAGES = find_packages(exclude=['tests', 'tests.*', 'dist', 'build'])

Expand Down

0 comments on commit f1482ff

Please sign in to comment.