From e4fa1aedde0de7c61039e1467ea69e7c404a002f Mon Sep 17 00:00:00 2001 From: Daniel Perna Date: Fri, 1 Jan 2021 15:29:30 +0100 Subject: [PATCH] 0.1.71 (#369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Homeassistant/improve/cover (#341) * enhanced device nameing * rmeoved unused var * Device Support for HmIP-DRSI1 (#356) * Add support for HmIPW-Devices (DRD3, SPI, DRBL4) (#310) * Added support for HmIPW-DRD3, HmIPW-SPI and HmIPW-DRBL4 * Fix whitespace Co-authored-by: Daniel Perna * support rx_mode (#313) * support rx_mode * fix whitespace * feat: add device support for HmIP-DRSI1 Co-authored-by: P0L0 <1452110+p0l0@users.noreply.github.com> Co-authored-by: Daniel Perna Co-authored-by: Steffen Rusitschka Co-authored-by: Lukas Riegel * Update misc.py (#363) * Update misc.py Added support for Wall Mounted Switch WRCC2 * Update misc.py * Update misc.py Starting to feel a bit stupid now, but now it should be correct * Add sensornode for tilt position of HmIP Covers (#365) * Support for Bat Level and WRCC2 (#366) Added battery voltage helper to remote HmIP devices Added WRCC2 remote switch. All tested and working with WRC2 and WRCC2 * Adds inhibit param to supported devices. (#364) * Bump version, update changelog * Lint * Support for HmIP-FALMOT-C12 #367 * Add HmIP-SRD #347 * Add to dict #347 * Logging cleanup Co-authored-by: Matthias Weiss Co-authored-by: lukasriegel Co-authored-by: P0L0 <1452110+p0l0@users.noreply.github.com> Co-authored-by: Steffen Rusitschka Co-authored-by: Lukas Riegel Co-authored-by: Remko76 <75678314+Remko76@users.noreply.github.com> Co-authored-by: Robin Blöhm Co-authored-by: Patrick Hofmann --- changelog.txt | 10 ++++++ manual_test.py | 6 +++- pyhomematic/_hm.py | 4 +++ pyhomematic/devicetypes/actors.py | 50 +++++++++++++++++++++--------- pyhomematic/devicetypes/generic.py | 16 +++++----- pyhomematic/devicetypes/helper.py | 27 ++++++++++++++++ pyhomematic/devicetypes/misc.py | 7 +++-- pyhomematic/devicetypes/sensors.py | 46 ++++++++++++++++++++++++--- setup.py | 2 +- 9 files changed, 136 insertions(+), 32 deletions(-) diff --git a/changelog.txt b/changelog.txt index 48164961a..875b141de 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,13 @@ +Verson 0.1.71 (2021-01-01) +- Improve naming of covers @weissm +- Add support for HmIP-DRSI1 (Issue #353) @lukasriegel +- Add support for HmIP-WRCC2 @Remko76 +- Implement INHIBIT parameter @PH89 +- Sensornode for HmIP blind actuators @rbloehm +- Add HmIP-FALMOT-C12 (Issue #367) @danielperna84 +- Add HmIP-SRD (Issue #347) @danielperna84 +- Set loglevel for getValue to Info + cleanup @danielperna84 + Version 0.1.70 (2020-10-03) - Remove toplevel tests (Issue #334) @onkelbeh diff --git a/manual_test.py b/manual_test.py index fe9409995..60d3c0a5c 100644 --- a/manual_test.py +++ b/manual_test.py @@ -4,7 +4,8 @@ import click from pyhomematic import HMConnection from pyhomematic.devicetypes.actors import GenericSwitch -from pyhomematic.devicetypes.helper import HelperLowBat, HelperSabotage, HelperWorking, HelperBatteryState, HelperValveState +from pyhomematic.devicetypes.helper import HelperLowBat, HelperSabotage, HelperWorking, HelperBatteryState, \ + HelperValveState, HelperInhibit from pyhomematic.devicetypes.sensors import WeatherSensor, AreaThermostat, ShutterContact, Smoke, Motion, Remote from pyhomematic.devicetypes.thermostats import HMThermostat, IPThermostat @@ -160,6 +161,9 @@ def cli(local, localport, remote, remoteport, address, channel, state, toggle, if isinstance(device, HelperWorking): print(" / Working: %s" % str(device.is_working())) + if isinstance(device, HelperInhibit): + print(" / Inhibit: %s" % str(device.get_inhibit())) + if isinstance(device, HelperValveState): print(" / Valve state: %i" % device.valve_state()) diff --git a/pyhomematic/_hm.py b/pyhomematic/_hm.py index 22505c126..d6440cf9d 100644 --- a/pyhomematic/_hm.py +++ b/pyhomematic/_hm.py @@ -453,6 +453,10 @@ def addDeviceNames(self, remote): if i.get('address') in self.devices[remote]: self.devices[remote][ i['address']].NAME = i['name'] + for channel_device_response in i['channels']: + name = channel_device_response['name'] + self.devices_all[remote][channel_device_response['address']].NAME = name + except Exception as err: LOG.warning( "RPCFunctions.addDeviceNames: Exception: %s" % str(err)) diff --git a/pyhomematic/devicetypes/actors.py b/pyhomematic/devicetypes/actors.py index ee5d3459a..66acdab5b 100644 --- a/pyhomematic/devicetypes/actors.py +++ b/pyhomematic/devicetypes/actors.py @@ -5,7 +5,8 @@ from pyhomematic.devicetypes.misc import HMEvent from pyhomematic.devicetypes.helper import ( HelperWorking, HelperActorState, HelperActorLevel, HelperActorBlindTilt, HelperActionOnTime, - HelperActionPress, HelperEventRemote, HelperWired, HelperRssiPeer, HelperRssiDevice, HelperDeviceTemperature) + HelperActionPress, HelperEventRemote, HelperWired, HelperRssiPeer, HelperRssiDevice, HelperDeviceTemperature, + HelperInhibit) LOG = logging.getLogger(__name__) @@ -34,7 +35,7 @@ def stop(self, channel=None): self.actionNodeData("STOP", True, channel) -class Blind(GenericBlind, HelperWorking, HelperRssiPeer): +class Blind(GenericBlind, HelperInhibit, HelperWorking, HelperRssiPeer): """ Blind switch that raises and lowers roller shutters or window blinds. """ @@ -105,6 +106,14 @@ def ELEMENT(self): class IPKeyBlindTilt(IPKeyBlind, HelperActorBlindTilt): + """ + Blind switch that raises, lowers and adjusts the slat position of shutters or blinds. + """ + def __init__(self, device_description, proxy, resolveparamsets=False): + super().__init__(device_description, proxy, resolveparamsets) + + # init metadata + self.SENSORNODE.update({"LEVEL_2": [3]}) def close_slats(self, channel=None): """Move the shutter up all the way.""" @@ -129,7 +138,7 @@ def off(self, channel=None): self.set_level(0.0, channel) -class Dimmer(GenericDimmer, HelperWorking): +class Dimmer(GenericDimmer, HelperInhibit, HelperWorking): """ Dimmer switch that controls level of light brightness. """ @@ -140,7 +149,7 @@ def ELEMENT(self): return [1] -class KeyDimmer(GenericDimmer, HelperWorking, HelperActionPress): +class KeyDimmer(GenericDimmer, HelperInhibit, HelperWorking, HelperActionPress): """ Dimmer switch that controls level of light brightness. """ @@ -205,7 +214,7 @@ def off(self, channel=None): self.set_state(False, channel) -class Rain(GenericSwitch, HelperWorking): +class Rain(GenericSwitch, HelperInhibit, HelperWorking): """Rain / Heat sensor with heating switch""" def __init__(self, device_description, proxy, resolveparamsets=False): super().__init__(device_description, proxy, resolveparamsets) @@ -221,7 +230,7 @@ def ELEMENT(self): return [2] -class Switch(GenericSwitch, HelperWorking, HelperRssiPeer): +class Switch(GenericSwitch, HelperInhibit, HelperWorking, HelperRssiPeer): """ Switch turning plugged in device on or off. """ @@ -241,7 +250,7 @@ def ELEMENT(self): return [13, 14, 15, 16, 17, 18, 19] return [1] -class IOSwitchWireless(GenericSwitch, HelperWorking, HelperEventRemote, HelperRssiPeer): +class IOSwitchWireless(GenericSwitch, HelperInhibit, HelperWorking, HelperEventRemote, HelperRssiPeer): """ Switch turning attached device on or off. Can controll relais and buttons independently. """ @@ -249,11 +258,11 @@ class IOSwitchWireless(GenericSwitch, HelperWorking, HelperEventRemote, HelperRs def ELEMENT(self): return [1, 2] -class IOSwitch(GenericSwitch, HelperWorking, HelperEventRemote, HelperWired): + +class IOSwitchNoInhibit(GenericSwitch, HelperWorking, HelperEventRemote, HelperWired): """ - Switch turning attached device on or off. + Switch turning attached device on or off and not having a inhibit function. """ - def __init__(self, device_description, proxy, resolveparamsets=False): self._doc = [] super().__init__(device_description, proxy, resolveparamsets) @@ -264,10 +273,20 @@ def __init__(self, device_description, proxy, resolveparamsets=False): @property def ELEMENT(self): - if "HMW-IO-12-Sw7-DR" in self.TYPE: - return [13, 14, 15, 16, 17, 18, 19] if "HMW-IO-12-FM" in self.TYPE: return self._doc + return [1] + + +class IOSwitch(GenericSwitch, HelperInhibit, HelperWorking, HelperEventRemote, HelperWired): + """ + Switch turning attached device on or off. + """ + + @property + def ELEMENT(self): + if "HMW-IO-12-Sw7-DR" in self.TYPE: + return [13, 14, 15, 16, 17, 18, 19] if "HMW-LC-Sw2-DR" in self.TYPE: return [3, 4] return [1] @@ -448,7 +467,7 @@ def ELEMENT(self): return [1] -class RFSiren(GenericSwitch, HelperWorking, HelperRssiPeer): +class RFSiren(GenericSwitch, HelperInhibit, HelperWorking, HelperRssiPeer): """ HM-Sec-Sir-WM Siren """ @@ -466,7 +485,7 @@ def ELEMENT(self): return [1, 2, 3] -class KeyMatic(HMActor, HelperActorState, HelperRssiPeer): +class KeyMatic(HMActor, HelperInhibit, HelperActorState, HelperRssiPeer): """ Lock, Unlock or Open KeyMatic. """ @@ -1014,7 +1033,7 @@ def set_color_temp(self, color_temp: float, channel=None): "HM-ES-PMSwX": SwitchPowermeter, "HMW-IO-12-Sw7-DR": IOSwitch, "HMW-IO-12-Sw14-DR": HMWIOSwitch, - "HMW-IO-12-FM": IOSwitch, + "HMW-IO-12-FM": IOSwitchNoInhibit, "HMW-LC-Sw2-DR": IOSwitch, "HB-LC-Sw2PBU-FM": IOSwitchWireless, "HMW-LC-Bl1-DR": KeyBlind, @@ -1037,6 +1056,7 @@ def set_color_temp(self, color_temp: float, channel=None): "HmIP-PCBS-BAT": IPSwitch, "HmIP-PMFS": IPSwitch, "HmIP-MOD-OC8": IPSwitch, + "HmIP-DRSI1": IPSwitch, "HmIP-DRSI4": IPSwitch, "HmIP-BSL": IPKeySwitchLevel, "HMIP-PSM": IPSwitchPowermeter, diff --git a/pyhomematic/devicetypes/generic.py b/pyhomematic/devicetypes/generic.py index 4434ee030..8c7e2c2ac 100644 --- a/pyhomematic/devicetypes/generic.py +++ b/pyhomematic/devicetypes/generic.py @@ -62,7 +62,7 @@ def event(self, interface_id, key, value): self._VALUES[key] = value # Cache the value for callback in self._eventcallbacks: - LOG.debug("HMGeneric.event: Using callback %s " % str(callback)) + LOG.debug("HMGeneric.event: Using callback %s", str(callback)) callback(self._ADDRESS, interface_id, key, value) def getParamsetDescription(self, paramset): @@ -72,7 +72,7 @@ def getParamsetDescription(self, paramset): try: self._PARAMSET_DESCRIPTIONS[paramset] = self._proxy.getParamsetDescription(self._ADDRESS, paramset) except Exception as err: - LOG.error("HMGeneric.getParamsetDescription: Exception: " + str(err)) + LOG.error("HMGeneric.getParamsetDescription: Exception: %s", err) return False def updateParamset(self, paramset): @@ -104,7 +104,7 @@ def updateParamsets(self): self.updateParamset(ps) return True except Exception as err: - LOG.error("HMGeneric.updateParamsets: Exception: " + str(err)) + LOG.error("HMGeneric.updateParamsets: Exception: %s", err) return False def putParamset(self, paramset, data={}, rx_mode=None): @@ -127,7 +127,7 @@ def putParamset(self, paramset, data={}, rx_mode=None): else: return False except Exception as err: - LOG.error("HMGeneric.putParamset: Exception: " + str(err)) + LOG.error("HMGeneric.putParamset: Exception: %s", err) return False @@ -190,7 +190,7 @@ def setValue(self, key, value): """ Some devices allow to directly set values to perform a specific task. """ - LOG.debug("HMGeneric.setValue: address = '%s', key = '%s' value = '%s'" % (self._ADDRESS, key, value)) + LOG.debug("HMGeneric.setValue: address = '%s', key = '%s' value = '%s'", self._ADDRESS, key, value) try: self._proxy.setValue(self._ADDRESS, key, value) return True @@ -203,14 +203,14 @@ def getValue(self, key): """ Some devices allow to directly get values for specific parameters. """ - LOG.debug("HMGeneric.getValue: address = '%s', key = '%s'" % (self._ADDRESS, key)) + LOG.debug("HMGeneric.getValue: address = '%s', key = '%s'", self._ADDRESS, key) try: returnvalue = self._proxy.getValue(self._ADDRESS, key) self._VALUES[key] = returnvalue return returnvalue except Exception as err: - LOG.warning("HMGeneric.getValue: %s on %s Exception: %s", key, - self._ADDRESS, err) + LOG.info("HMGeneric.getValue: %s on %s Exception: %s", key, + self._ADDRESS, err) return False diff --git a/pyhomematic/devicetypes/helper.py b/pyhomematic/devicetypes/helper.py index 518a2d4e8..c2fb311ee 100644 --- a/pyhomematic/devicetypes/helper.py +++ b/pyhomematic/devicetypes/helper.py @@ -168,6 +168,33 @@ def set_level(self, position, channel=None): self.writeNodeData("LEVEL", position, channel) + +class HelperInhibit(HMDevice): + """ + Generic inhibit functions + Inhibit causes an actor to be in locked state, refusing to accept commands except from CCU or API + This is only supported for HomeMatic components NOT for HomeMatic IP. + """ + def __init__(self, device_description, proxy, resolveparamsets=False): + super().__init__(device_description, proxy, resolveparamsets) + + # init metadata + self.WRITENODE.update({"INHIBIT": self.ELEMENT}) + + def get_inhibit(self, channel=None): + """Return current inhibit status. Return value is 'on' or 'off'.""" + return self.getWriteData("INHIBIT", channel) + + def set_inhibit(self, inhibit, channel=None): + """Turn inhibit 'on' or 'off'.""" + try: + inhibit = bool(inhibit) + except Exception as err: + LOG.debug("HelperInhibit.set_inhibit: Exception %s" % (err,)) + return False + self.writeNodeData("INHIBIT", inhibit, channel) + + class HelperActorBlindTilt(HMDevice): """ Generic shutter level functions diff --git a/pyhomematic/devicetypes/misc.py b/pyhomematic/devicetypes/misc.py index e29330e5b..5b2c05dce 100644 --- a/pyhomematic/devicetypes/misc.py +++ b/pyhomematic/devicetypes/misc.py @@ -2,7 +2,7 @@ from pyhomematic.devicetypes.generic import HMDevice from pyhomematic.devicetypes.helper import HelperActionPress, \ HelperEventRemote, HelperEventPress, HelperRssiPeer, HelperLowBatIP, \ - HelperLowBat + HelperLowBat, HelperOperatingVoltageIP LOG = logging.getLogger(__name__) @@ -28,7 +28,7 @@ class Remote(HMEvent, HelperEventRemote, HelperActionPress, HelperRssiPeer): @property def ELEMENT(self): - if "RC-2" in self.TYPE or "PB-2" in self.TYPE or "WRC2" in self.TYPE or "BRC2" in self.TYPE: + if "RC-2" in self.TYPE or "PB-2" in self.TYPE or "WRC2" in self.TYPE or "BRC2" in self.TYPE or "WRCC2" in self.TYPE: return [1, 2] if "HM-Dis-WM55" in self.TYPE or "HM-Dis-EP-WM55" in self.TYPE: return [1, 2] @@ -61,7 +61,7 @@ def ELEMENT(self): return [1] -class RemoteBatteryIP(Remote, HelperLowBatIP): +class RemoteBatteryIP(Remote, HelperLowBatIP, HelperOperatingVoltageIP): """Battery operated HomeMaticIP remote device.""" @@ -138,6 +138,7 @@ def ELEMENT(self): "HMW-IO-4-FM": Remote, "HMIP-WRC2": RemoteBatteryIP, "HmIP-WRC2": RemoteBatteryIP, + "HmIP-WRCC2": RemoteBatteryIP, "HmIP-BRC2": Remote, "HmIP-WRC6": RemoteBatteryIP, "HmIP-WRCD": RemoteBatteryIP, diff --git a/pyhomematic/devicetypes/sensors.py b/pyhomematic/devicetypes/sensors.py index 6a63181a2..35bc20880 100644 --- a/pyhomematic/devicetypes/sensors.py +++ b/pyhomematic/devicetypes/sensors.py @@ -795,6 +795,26 @@ def is_temperature_out_of_range(self, channel=None): return bool(self.getAttributeData("TEMPERATURE_OUT_OF_RANGE", channel)) +class IPRainSensor(SensorHmIP): + """HomeMatic IP Rain sensor HmIP-SRD.""" + + def __init__(self, device_description, proxy, resolveparamsets=False): + super().__init__(device_description, proxy, resolveparamsets) + + # init metadata + self.SENSORNODE.update({"ACTUAL_TEMPERATURE": [1]}) + self.BINARYNODE.update({"RAINING": [1], + "HEATER_STATE": [1]}) + self.ATTRIBUTENODE.update({"ERROR_CODE": [0], + "OPERATING_VOLTAGE": [0]}) + + def get_temperature(self, channel=None): + return float(self.getSensorData("ACTUAL_TEMPERATURE", channel)) + + def is_raining(self, channel=None): + return bool(self.getBinaryData("RAINING", channel)) + + class IPPassageSensor(SensorHmIP, HelperRssiPeer, HelperEventRemote): """HomeMatic IP Passage Sensor. #2 = right to left, #3 = left to right This is a binary sensor.""" @@ -1002,6 +1022,22 @@ def is_optical_alarm_active(self, channel=None): def is_acoustic_alarm_active(self, channel=None): return bool(self.getBinaryData("ACOUSTIC_ALARM_ACTIVE", channel)) +class ValveBox(SensorHmIP): + """Valve Box HmIP-FALMOT-C12""" + + def __init__(self, device_description, proxy, resolveparamsets=False): + super().__init__(device_description, proxy, resolveparamsets) + + self.SENSORNODE.update({"LEVEL": self.ELEMENT}) + + def get_level(self, channel=None): + """Return valve state from 0% to 99%""" + return float(self.getSensorData("LEVEL", channel)) + + @property + def ELEMENT(self): + return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + DEVICETYPES = { "HM-Sec-SC": ShutterContact, "HM-Sec-SC-2": ShutterContact, @@ -1102,8 +1138,10 @@ def is_acoustic_alarm_active(self, channel=None): "HmIP-DSD-PCB": IPContact, "HB-UNI-Sen-TEMP-DS18B20": TemperatureSensor, "HB-UNI-Sen-WEA": HBUNISenWEA, - "HmIP-ASIR-B1": IPAlarmSensor, - "HmIP-ASIR-O": IPAlarmSensor, - "HmIP-ASIR": IPAlarmSensor, - "HmIP-ASIR-2": IPAlarmSensor, + "HmIP-ASIR-B1": IPAlarmSensor, + "HmIP-ASIR-O": IPAlarmSensor, + "HmIP-ASIR": IPAlarmSensor, + "HmIP-ASIR-2": IPAlarmSensor, + "HmIP-FALMOT-C12": ValveBox, + "HmIP-SRD": IPRainSensor, } diff --git a/setup.py b/setup.py index 41dacf2df..eab5f50c4 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): PACKAGE_NAME = 'pyhomematic' HERE = os.path.abspath(os.path.dirname(__file__)) -VERSION = '0.1.70' +VERSION = '0.1.71' PACKAGES = find_packages(exclude=['dist', 'build', 'tests'])