Skip to content

Commit

Permalink
Merge pull request #278 from coreGreenberet/ALARM_SIREN_OUTDOOR_and_H…
Browse files Browse the repository at this point in the history
…OT_WATER

Alarm siren outdoor and hot water
  • Loading branch information
coreGreenberet authored Dec 22, 2019
2 parents e0ab086 + 5f217ac commit 90a224a
Show file tree
Hide file tree
Showing 15 changed files with 281 additions and 47 deletions.
16 changes: 11 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Added
- Devices
- HMIP-MOD-TM (Garage Door Module for Novoferm and Tormatic door operators)
- API
- FunctionalChannels: DOOR_CHANNEL
- Python 3.8 support
- API
- FunctionalChannels:
- DOOR_CHANNEL
- DEVICE_RECHARGEABLE_WITH_SABOTAGE
- ExtendedLinkedShutterGroup.set_slats_level
- added missing attributes
- Devices
- HMIP-MOD-TM (Garage Door Module for Novoferm and Tormatic door operators)
- HMIP-ASIR-O (Alarm Siren - outdoor)

- Groups
- HOT_WATER
- Python 3.8 support

### Changed
- General
- removed homematicip-testing package. Pip will automatically install the latest tagged release. For a "nightly" build you just have to run it with the "--pre" argument.
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Homematic IP Devices:
- [X] HMIP-ASIR (Alarm Siren - indoor)
- [X] HMIP-ASIR-B1 (Alarm Siren - indoor) *Silvercrest Edition*
- [X] HMIP-ASIR-2 (Alarm Siren2 - indoor) New Version
- [ ] HMIP-ASIR-O (Alarm Siren - outdoor)
- [X] HMIP-ASIR-O (Alarm Siren - outdoor)
- [X] HMIP-BBL (Blind Actuator for brand switches)
- [X] HMIP-BDT (Dimming Actuator for brand switches)
- [X] HMIP-BRC2 (Remote Control for brand switches – 2x channels)
Expand Down
2 changes: 2 additions & 0 deletions homematicip/aio/class_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
DeviceType.DEVICE: AsyncDevice,
DeviceType.ACCELERATION_SENSOR: AsyncAccelerationSensor,
DeviceType.ALARM_SIREN_INDOOR: AsyncAlarmSirenIndoor,
DeviceType.ALARM_SIREN_OUTDOOR: AsyncAlarmSirenOutdoor,
DeviceType.BRAND_BLIND: AsyncBrandBlind,
DeviceType.BRAND_DIMMER: AsyncBrandDimmer,
DeviceType.BRAND_SHUTTER: AsyncFullFlushShutter,
Expand Down Expand Up @@ -81,6 +82,7 @@
GroupType.HEATING_COOLING_DEMAND_PUMP: AsyncHeatingCoolingDemandPumpGroup,
GroupType.HEATING_FAILURE_ALERT_RULE_GROUP: AsyncHeatingFailureAlertRuleGroup,
GroupType.HUMIDITY_WARNING_RULE_GROUP: AsyncHumidityWarningRuleGroup,
GroupType.HOT_WATER: AsyncHotWaterGroup,
GroupType.SWITCHING_PROFILE: AsyncSwitchingProfileGroup,
GroupType.OVER_HEAT_PROTECTION_RULE: AsyncOverHeatProtectionRule,
GroupType.SMOKE_ALARM_DETECTION_RULE: AsyncSmokeAlarmDetectionRule,
Expand Down
4 changes: 4 additions & 0 deletions homematicip/aio/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ class AsyncAlarmSirenIndoor(AlarmSirenIndoor, AsyncSabotageDevice):
""" HMIP-ASIR (Alarm Siren) """


class AsyncAlarmSirenOutdoor(AlarmSirenOutdoor, AsyncAlarmSirenIndoor):
""" HMIP-ASIR-O (Alarm Siren Outdoor) """


class AsyncMotionDetectorIndoor(MotionDetectorIndoor, AsyncSabotageDevice):
""" HMIP-SMI (Motion Detector with Brightness Sensor - indoor) """

Expand Down
6 changes: 6 additions & 0 deletions homematicip/aio/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
ShutterWindProtectionRule,
LockOutProtectionRule,
EnvironmentGroup,
HotWaterGroup,
)

from homematicip.base.enums import *
Expand Down Expand Up @@ -243,3 +244,8 @@ class AsyncLockOutProtectionRule(LockOutProtectionRule, AsyncGroup):

class AsyncEnvironmentGroup(EnvironmentGroup, AsyncGroup):
pass


class AsyncHotWaterGroup(HotWaterGroup, AsyncGroup):
async def set_profile_mode(self, profileMode: ProfileMode):
return await self._connection.api_call(*super().set_profile_mode(profileMode))
8 changes: 8 additions & 0 deletions homematicip/base/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class DeviceType(AutoNameEnum):
DEVICE = auto()
ACCELERATION_SENSOR = auto()
ALARM_SIREN_INDOOR = auto()
ALARM_SIREN_OUTDOOR = auto()
BRAND_BLIND = auto()
BRAND_DIMMER = auto()
BRAND_PUSH_BUTTON = auto()
Expand Down Expand Up @@ -268,6 +269,7 @@ class GroupType(AutoNameEnum):
HEATING_EXTERNAL_CLOCK = auto()
HEATING_FAILURE_ALERT_RULE_GROUP = auto()
HEATING = auto()
HOT_WATER = auto()
HUMIDITY_WARNING_RULE_GROUP = auto()
SECURITY_ZONE = auto()
INBOX = auto()
Expand Down Expand Up @@ -378,6 +380,7 @@ class FunctionalChannelType(AutoNameEnum):
DEVICE_INCORRECT_POSITIONED = auto()
DEVICE_OPERATIONLOCK = auto()
DEVICE_PERMANENT_FULL_RX = auto()
DEVICE_RECHARGEABLE_WITH_SABOTAGE = auto()
DEVICE_SABOTAGE = auto()
DOOR_CHANNEL = auto()
DIMMER_CHANNEL = auto()
Expand Down Expand Up @@ -502,3 +505,8 @@ class GroupVisibility(AutoNameEnum):
INVISIBLE_GROUP_AND_CONTROL = auto()
INVISIBLE_CONTROL = auto()
VISIBLE = auto()


class ProfileMode(AutoNameEnum):
AUTOMATIC = auto()
MANUAL = auto()
13 changes: 13 additions & 0 deletions homematicip/base/functionalChannels.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,3 +748,16 @@ def from_json(self, js, groups: Iterable[Group]):
self.set_attr_from_dict(
"notificationSoundTypeLowToHigh", js, NotificationSoundType
)


class DeviceRechargeableWithSabotage(DeviceSabotageChannel):
""" this is the representative of the DEVICE_RECHARGEABLE_WITH_SABOTAGE channel"""

def __init__(self):
super().__init__()
#:bool:is the battery in a bad condition
self.badBatteryHealth = False

def from_json(self, js, groups: Iterable[Group]):
super().from_json(js, groups)
self.set_attr_from_dict("badBatteryHealth", js)
3 changes: 3 additions & 0 deletions homematicip/class_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DeviceType.DEVICE: Device,
DeviceType.ACCELERATION_SENSOR: AccelerationSensor,
DeviceType.ALARM_SIREN_INDOOR: AlarmSirenIndoor,
DeviceType.ALARM_SIREN_OUTDOOR: AlarmSirenOutdoor,
DeviceType.BRAND_BLIND: BrandBlind,
DeviceType.BRAND_DIMMER: BrandDimmer,
DeviceType.BRAND_PUSH_BUTTON: BrandPushButton,
Expand Down Expand Up @@ -87,6 +88,7 @@
GroupType.HEATING_FAILURE_ALERT_RULE_GROUP: HeatingFailureAlertRuleGroup,
GroupType.HEATING_COOLING_DEMAND_BOILER: HeatingCoolingDemandBoilerGroup,
GroupType.HEATING_COOLING_DEMAND_PUMP: HeatingCoolingDemandPumpGroup,
GroupType.HOT_WATER: HotWaterGroup,
GroupType.HUMIDITY_WARNING_RULE_GROUP: HumidityWarningRuleGroup,
GroupType.SWITCHING_PROFILE: SwitchingProfileGroup,
GroupType.OVER_HEAT_PROTECTION_RULE: OverHeatProtectionRule,
Expand Down Expand Up @@ -136,6 +138,7 @@
FunctionalChannelType.DEVICE_INCORRECT_POSITIONED: DeviceIncorrectPositionedChannel,
FunctionalChannelType.DEVICE_OPERATIONLOCK: DeviceOperationLockChannel,
FunctionalChannelType.DEVICE_PERMANENT_FULL_RX: DevicePermanentFullRxChannel,
FunctionalChannelType.DEVICE_RECHARGEABLE_WITH_SABOTAGE: DeviceRechargeableWithSabotage,
FunctionalChannelType.DEVICE_SABOTAGE: DeviceSabotageChannel,
FunctionalChannelType.DOOR_CHANNEL: DoorChannel,
FunctionalChannelType.DIMMER_CHANNEL: DimmerChannel,
Expand Down
55 changes: 41 additions & 14 deletions homematicip/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def __init__(self, connection):
0 # firmwareVersion = A.B.C -> firmwareVersionInteger ((A<<16)|(B<<8)|C)
)
self.availableFirmwareVersion = None
self.unreach = None
self.lowBat = None
self.unreach = False
self.lowBat = False
self.routerModuleSupported = False
self.routerModuleEnabled = False
self.modelType = ""
Expand All @@ -55,10 +55,13 @@ def __init__(self, connection):

self._baseChannel = "DEVICE_BASE"

self.deviceOverheated = None
self.deviceOverloaded = None
self.deviceUndervoltage = None
self.temperatureOutOfRange = None
self.deviceOverheated = False
self.deviceOverloaded = False
self.deviceUndervoltage = False
self.temperatureOutOfRange = False
self.coProFaulty = False
self.coProRestartNeeded = False
self.coProUpdateFailure = False

def from_json(self, js):
super().from_json(js)
Expand Down Expand Up @@ -94,14 +97,20 @@ def from_json(self, js):

sof = c.get("supportedOptionalFeatures")
if sof:
if sof["IFeatureDeviceOverheated"]:
self.deviceOverheated = c["deviceOverheated"]
if sof["IFeatureDeviceOverloaded"]:
self.deviceOverloaded = c["deviceOverloaded"]
if sof["IFeatureDeviceUndervoltage"]:
self.deviceUndervoltage = c["deviceUndervoltage"]
if sof["IFeatureDeviceTemperatureOutOfRange"]:
self.temperatureOutOfRange = c["temperatureOutOfRange"]
if sof.get("IFeatureDeviceOverheated", False):
self.set_attr_from_dict("deviceOverheated", c)
if sof.get("IFeatureDeviceOverloaded", False):
self.set_attr_from_dict("deviceOverloaded", c)
if sof.get("IFeatureDeviceUndervoltage", False):
self.set_attr_from_dict("deviceUndervoltage", c)
if sof.get("IFeatureDeviceTemperatureOutOfRange", False):
self.set_attr_from_dict("temperatureOutOfRange", c)
if sof.get("IFeatureDeviceCoProError", False):
self.set_attr_from_dict("coProFaulty", c)
if sof.get("IFeatureDeviceCoProRestart", False):
self.set_attr_from_dict("coProRestartNeeded", c)
if sof.get("IFeatureDeviceCoProUpdate", False):
self.set_attr_from_dict("coProUpdateFailure", c)

def __str__(self):
return "{} {} lowbat({}) unreach({}) rssiDeviceValue({}) rssiPeerValue({}) configPending({}) dutyCycle({})".format(
Expand Down Expand Up @@ -780,6 +789,24 @@ def from_json(self, js):
pass


class AlarmSirenOutdoor(AlarmSirenIndoor):
""" HMIP-ASIR-O (Alarm Siren Outdoor) """

def __init__(self, connection):
super().__init__(connection)
self.badBatteryHealth = False
self._baseChannel = "DEVICE_RECHARGEABLE_WITH_SABOTAGE"

def from_json(self, js):
super().from_json(js)
c = get_functional_channel("DEVICE_RECHARGEABLE_WITH_SABOTAGE", js)
if c:
self.set_attr_from_dict("badBatteryHealth", c)

def __str__(self):
return f"{super().__str__()} badBatteryHealth({self.badBatteryHealth})"


class MotionDetectorIndoor(SabotageDevice):
""" HMIP-SMI (Motion Detector with Brightness Sensor - indoor) """

Expand Down
37 changes: 31 additions & 6 deletions homematicip/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,14 +919,14 @@ def __init__(self, connection):
self.profileId = (
None # Not sure why it is there. You can't use it to query something.
)
self.profileMode = None
self.profileMode = ProfileMode.MANUAL

def from_json(self, js, devices):
super().from_json(js, devices)
self.on = js["on"]
self.dimLevel = js["dimLevel"]
self.profileId = js["profileId"]
self.profileMode = js["profileMode"]
self.set_attr_from_dict("on", js)
self.set_attr_from_dict("dimLevel", js)
self.set_attr_from_dict("profileId", js)
self.set_attr_from_dict("profileMode", js, ProfileMode)

def __str__(self):
return "{} on({}) dimLevel({}) profileMode({})".format(
Expand All @@ -949,7 +949,7 @@ def set_profile_mode(self, devices, automatic=True):
data = {
"groupId": self.id,
"channels": channels,
"profileMode": "AUTOMATIC" if automatic else "MANUAL",
"profileMode": ProfileMode.AUTOMATIC if automatic else ProfileMode.MANUAL,
}
return self._restCall(
"group/switching/profile/setProfileMode", body=json.dumps(data)
Expand Down Expand Up @@ -1077,3 +1077,28 @@ def __str__(self):
self.windSpeed,
self.humidity,
)


class HotWaterGroup(Group):
def __init__(self, connection):
super().__init__(connection)
self.on = None
self.onTime = 0.0
self.profileId = (
None # Not sure why it is there. You can't use it to query something.
)
self.profileMode = ProfileMode.MANUAL

def from_json(self, js, devices):
super().from_json(js, devices)
self.set_attr_from_dict("on", js)
self.set_attr_from_dict("onTime", js)
self.set_attr_from_dict("profileId", js)
self.set_attr_from_dict("profileMode", js, ProfileMode)

def __str__(self):
return f"{super().__str__()} on({self.on}) onTime({self.onTime}) profileMode({self.profileMode})"

def set_profile_mode(self, profileMode: ProfileMode):
data = {"groupId": self.id, "profileMode": profileMode}
return self._restCall("group/heating/setProfileMode", body=json.dumps(data))
11 changes: 11 additions & 0 deletions homematicip_demo/fake_cloud_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,17 @@ async def post_hmip_group_heating_setControlMode(
response = self.errorCode("INVALID_GROUP", 404)
return response

async def post_hmip_group_heating_setProfileMode(
self, request: web.Request
) -> web.Response:
response = web.json_response(None)

js = json.loads(request.data)
g = self.data["groups"][js["groupId"]]
g["profileMode"] = js["profileMode"]

return response

@validate_authorization
async def post_hmip_group_switching_alarm_testSignalAcoustic(
self, request: web.Request
Expand Down
Loading

0 comments on commit 90a224a

Please sign in to comment.