def testGetDeviceByEvent_invalidEvent_returnsNone(self): zone = Zone('ff', [self.light]) eventInfo = EventInfo(ZoneEvent.MOTION, self.motionSensorItem, zone, MockedZoneManager([zone]), self.getMockedEventDispatcher()) self.assertEqual(None, zone.getDeviceByEvent(eventInfo))
def createTestData(self, excludedDevices=[], extraIncludedDevices=[]): ''' :return: a list of two zones, the mocked zone manager, and the event dispatcher :rtype: list ''' self.partition.armAway(self.getMockedEventDispatcher()) porch = Zone.createExternalZone('porch').addDevice(self.partition) greatRoom = Zone("GR", [self.audioSink], Level.FIRST_FLOOR) for d in excludedDevices: if porch.hasDevice(d): porch = porch.removeDevice(d) if greatRoom.hasDevice(d): greatRoom = greatRoom.removeDevice(d) for d in extraIncludedDevices: greatRoom = greatRoom.addDevice(d) zm = MockedZoneManager([porch, greatRoom]) eventInfo = EventInfo(ZoneEvent.MOTION, ITEMS[0], porch, zm, self.getMockedEventDispatcher()) return [porch, greatRoom, zm, eventInfo]
def testOnAction_zoneIsExternal_returnsFalse(self): zone = Zone.createExternalZone('porch').addDevice( TemperatureSensor(ITEMS[0])) eventInfo = EventInfo(ZoneEvent.TEMPERATURE_CHANGED, ITEMS[0], zone, None, self.getMockedEventDispatcher()) value = AlertOnTemperatureOutOfRange().onAction(eventInfo) self.assertFalse(value)
def onSwitchTurnedOff(self, events, item, immutableZoneManager): ''' If item belongs to this zone, dispatches the event to the associated Switch object, execute the associated actions, and returns True. Otherwise return False. See :meth:`.Switch.onSwitchTurnedOff` :rtype: boolean ''' eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_OFF, item, self, immutableZoneManager, events) isProcessed = False actions = self.getActions(ZoneEvent.SWITCH_TURNED_OFF) switches = self.getDevicesByType(Switch) for switch in switches: if switch.onSwitchTurnedOff(events, item.getName()): for a in actions: a.onAction(eventInfo) isProcessed = True return isProcessed
def testOnAction_masterIsOn_returnsTrueAndNotTurningOffOpenSpaceNeighbor( self): self.illuminanceSensorItem.setState( DecimalType(ILLUMINANCE_THRESHOLD_IN_LUX - 1)) # zone3 (foyer) is an open space neighbor with zone2 self.zone2 = self.zone2.addNeighbor( Neighbor(self.zone3.getId(), NeighborType.OPEN_SPACE)) # zone2 (kitchen) is an open space slave with zone1 (great room) self.zone2 = self.zone2.addNeighbor( Neighbor(self.zone1.getId(), NeighborType.OPEN_SPACE_MASTER)) # Turn on the light in the great room and the foyer. # We want to make sure that when the motion sensor in the kitchen is # triggered, it won't be turn on, and also the foyer light must not # be turned off. # The rationale is that someone just open the door to come to the foyer # area. However, as the great room light was already on, that indicates # someone is already in that area. As such, any movement in that # area must not prematurely turn off the the foyer light. self.lightItem1.setState(scope.OnOffType.ON) self.lightItem3.setState(scope.OnOffType.ON) eventInfo = EventInfo(ZoneEvent.MOTION, ITEMS[0], self.zone2, self.createMockedZoneManager(), self.getMockedEventDispatcher()) returnVal = TurnOnSwitch().onAction(eventInfo) self.assertFalse(returnVal) self.assertFalse(self.zone2.isLightOn()) self.assertTrue(self.zone3.isLightOn())
def testGetDeviceByEvent_validEvent_returnsExpectedDevice(self): zone = Zone('ff', [self.light]) eventInfo = EventInfo(ZoneEvent.MOTION, self.lightItem, zone, MockedZoneManager([zone]), self.getMockedEventDispatcher()) self.assertEqual(self.light, zone.getDeviceByEvent(eventInfo))
def sendEventAndAssertNoAlert(self): AlertManager.reset() eventInfo = EventInfo(ZoneEvent.HUMIDITY_CHANGED, ITEMS[0], self.zone1, None, self.getMockedEventDispatcher()) value = self.action.onAction(eventInfo) self.assertTrue(value) self.assertEqual(None, AlertManager._lastEmailedSubject)
def sendEventAndAssertAlertContainMessage(self, message): AlertManager.reset() eventInfo = EventInfo(ZoneEvent.HUMIDITY_CHANGED, ITEMS[0], self.zone1, None, self.getMockedEventDispatcher()) value = self.action.onAction(eventInfo) self.assertTrue(value) self.assertTrue(message in AlertManager._lastEmailedSubject)
def testOnAction_switchOffEvent_pauseStreamAndReturnsTrue(self): zone1 = Zone('shower').addDevice(self.sink) eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_OFF, ITEMS[0], zone1, None, scope.events) value = self.action.onAction(eventInfo) self.assertTrue(value) self.assertEqual('pause', self.sink._getLastTestCommand())
def testOnAction_switchOnEventAndAudioSinkInZone_playsStreamAndReturnsTrue( self): zone1 = Zone('shower').addDevice(self.sink) eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_ON, ITEMS[0], zone1, None, scope.events) value = self.action.onAction(eventInfo) self.assertTrue(value) self.assertEqual('playStream', self.sink._getLastTestCommand())
def testOnAction_doorClosedWithNoPresenceEvent_armAndReturnsTrue(self): ITEMS[0].setState(scope.OnOffType.OFF) # close door self.alarmPartition.disarm(self.getMockedEventDispatcher()) eventInfo = EventInfo(ZoneEvent.CONTACT_CLOSED, ITEMS[0], self.zone1, self.mockZoneManager, self.getMockedEventDispatcher()) value = ArmAfterFrontDoorClosed(0.1).onAction(eventInfo) self.assertTrue(value) time.sleep(0.2) self.assertTrue(self.alarmPartition.isArmedAway())
def testOnAction_aDoorIsOpen_returnsTrue(self): ITEMS[0].setState(scope.OnOffType.ON) eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], self.zone1, None, self.getMockedEventDispatcher()) action = AlertOnExternalDoorLeftOpen(0.1) value = action.onAction(eventInfo) self.assertTrue(value) self.assertTrue(action.hasRunningTimer()) time.sleep(0.3) # wait for the production code timer self.assertTrue("door" in AlertManager._lastEmailedSubject)
def testOnAction_switchOnEventAndAudioSinkInNeighborZone_playsStreamAndReturnsTrue( self): zone1 = Zone('shower') zone2 = Zone('washroom').addDevice(self.sink) zone1 = zone1.addNeighbor( Neighbor(zone2.getId(), NeighborType.OPEN_SPACE)) eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_ON, ITEMS[0], zone1, MockedZoneManager([zone1, zone2]), scope.events) value = self.action.onAction(eventInfo) self.assertTrue(value) self.assertEqual('playStream', self.sink._getLastTestCommand())
def createTestData(self, zoneEvent): ''' :return: a list of two zones, the mocked zone manager, and the event dispatcher :rtype: list ''' self.partition.armAway(self.getMockedEventDispatcher()) zone = Zone('porch', [self.partition, self.light, self.audioSink]) zm = MockedZoneManager([zone]) eventInfo = EventInfo(zoneEvent, ITEMS[0], zone, zm, self.getMockedEventDispatcher()) return [zone, zm, eventInfo]
def testOnAction_doorClosedWithPresenceEvent_notArmedAndReturnsTrue(self): ITEMS[0].setState(scope.OnOffType.OFF) # close door self.alarmPartition.disarm(self.getMockedEventDispatcher()) eventInfo = EventInfo(ZoneEvent.CONTACT_CLOSED, ITEMS[0], self.zone1, self.mockZoneManager, self.getMockedEventDispatcher()) value = ArmAfterFrontDoorClosed(0.1).onAction(eventInfo) self.assertTrue(value) time.sleep(0.1) # simulate a motion event self.internalMotionSensor._updateLastActivatedTimestamp() time.sleep(0.1) self.assertFalse(self.alarmPartition.isArmedAway())
def testOnAction_motionTriggeredInAnExternalZone_ignoreMotionEventAndContinueToArm(self): ITEMS[0].setState(scope.OnOffType.OFF) # close door self.alarmPartition.disarm(self.getMockedEventDispatcher()) eventInfo = EventInfo(ZoneEvent.CONTACT_CLOSED, ITEMS[0], self.zone1, self.mockZoneManager, self.getMockedEventDispatcher()) value = ArmAfterFrontDoorClosed(0.1).onAction(eventInfo) self.assertTrue(value) time.sleep(0.1) # simulate a motion event self.externalMotionSensor._updateLastActivatedTimestamp() time.sleep(0.2) self.assertTrue(self.alarmPartition.isArmedAway())
def _invokeActions(self, zoneEventType, eventDispatcher, item, immutableZoneManager): ''' Helper method to invoke actions associated with the event. :return: True if event is processed. :rtype: boolean ''' eventInfo = EventInfo(zoneEventType, item, self, immutableZoneManager, eventDispatcher) processed = False for a in self.getActions(zoneEventType): if a.onAction(eventInfo): processed = True return processed
def testOnAction_aDoorWasOpenButClosedSoonAfter_returnsTrueAndTimerCancelled( self): ITEMS[0].setState(scope.OnOffType.ON) eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], self.zone1, None, self.getMockedEventDispatcher()) action = AlertOnExternalDoorLeftOpen() value = action.onAction(eventInfo) self.assertTrue(value) self.assertTrue(action.hasRunningTimer()) # simulate door closed ITEMS[0].setState(scope.OnOffType.OFF) value = action.onAction(eventInfo) self.assertTrue(value) self.assertFalse(action.hasRunningTimer())
def turnOn(self): eventInfo = EventInfo(ZoneEvent.MOTION, ITEMS[0], self.zone1, self.createMockedZoneManager(), self.getMockedEventDispatcher()) return TurnOnSwitch().onAction(eventInfo)
def testOnAction_notAnExternalZone_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], Zone('innerZone'), None, self.getMockedEventDispatcher()) value = AlertOnExternalDoorLeftOpen().onAction(eventInfo) self.assertFalse(value)
def testOnAction_zoneIsExternal_returnsFalse(self): zone = Zone.createExternalZone('porch').addDevice(HumiditySensor(ITEMS[0])) eventInfo = EventInfo(ZoneEvent.HUMIDITY_CHANGED, ITEMS[0], zone, None, self.getMockedEventDispatcher()) value = AlertOnHumidityOutOfRange().onAction(eventInfo) self.assertFalse(value)
def testOnAction_zoneDoesNotContainSensor_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.HUMIDITY_CHANGED, ITEMS[0], Zone('innerZone'), None, self.getMockedEventDispatcher()) value = AlertOnHumidityOutOfRange().onAction(eventInfo) self.assertFalse(value)
def testOnAction_externalZoneWithNoDoor_returnsFalseAndTimerStarted(self): eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], Zone.createExternalZone('aZone'), None, self.getMockedEventDispatcher()) value = AlertOnExternalDoorLeftOpen().onAction(eventInfo) self.assertFalse(value)
def testOnAction_wrongEventType_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], Zone('innerZone'), None, scope.events) value = self.action.onAction(eventInfo) self.assertFalse(value)
def turnOff(self, zone): eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_ON, ITEMS[0], zone, self.zoneManager, self.getMockedEventDispatcher()) return TurnOffAdjacentZones().onAction(eventInfo)
def onAction(self, eventInfo): events = eventInfo.getEventDispatcher() zone = eventInfo.getZone() zoneManager = eventInfo.getZoneManager() isProcessed = False canTurnOffAdjacentZones = True lightOnTime = zone.isLightOnTime() zoneIlluminance = zone.getIlluminanceLevel() for switch in zone.getDevicesByType(Switch): if switch.isOn(): switch.turnOn(events) # renew the timer if a switch is already on isProcessed = True canTurnOffAdjacentZones = False continue if not switch.canBeTriggeredByMotionSensor(): # A special case: if a switch is configured not to be # triggered by a motion sensor, it means there is already # another switch sharing that motion sensor. In this case, we # don't want to turn off the other switch. canTurnOffAdjacentZones = False if DEBUG: PE.logInfo("{}: rejected - can't be triggerred by motion sensor".format( switch.getItemName())) continue # Break if switch was just turned off. if None != switch.getLastOffTimestampInSeconds(): if (time.time() - switch.getLastOffTimestampInSeconds()) <= \ TurnOnSwitch.DELAY_AFTER_LAST_OFF_TIME_IN_SECONDS: if DEBUG: PE.logInfo("{}: rejected - switch was just turned off".format( switch.getItemName())) continue # Break if the switch of a neighbor sharing the motion sensor was # just turned off. openSpaceZones = [zoneManager.getZoneById(n.getZoneId()) \ for n in zone.getNeighbors() if n.isOpenSpace()] sharedMotionSensorZones = [z for z in openSpaceZones if zone.shareSensorWith(z, MotionSensor)] theirSwitches = reduce(lambda a, b : a + b, [z.getDevicesByType(Switch) for z in sharedMotionSensorZones], []) if any(time.time() - s.getLastOffTimestampInSeconds() <= \ TurnOnSwitch.DELAY_AFTER_LAST_OFF_TIME_IN_SECONDS \ for s in theirSwitches): if DEBUG: PE.logInfo("{}: rejected - can't be triggerred by motion sensor".format( switch.getItemName())) continue if isinstance(switch, Light): if lightOnTime or switch.isLowIlluminance(zoneIlluminance): isProcessed = True if isProcessed and None != zoneManager: masterZones = [zoneManager.getZoneById(n.getZoneId()) \ for n in zone.getNeighbors() \ if NeighborType.OPEN_SPACE_MASTER == n.getType()] if any(z.isLightOn() for z in masterZones): isProcessed = False # This scenario indicates that there is already # activity in the master zone, and thus such activity # must not prematurely turns off the light in the # adjacent zone. canTurnOffAdjacentZones = False if DEBUG: PE.logInfo("{}: rejected - a master zone's light is on".format( switch.getItemName())) if isProcessed: switch.turnOn(events) else: switch.turnOn(events) isProcessed = True # Now shut off the light in any shared space zones if canTurnOffAdjacentZones: if DEBUG: PE.logInfo("{}: turning off adjancent zone's light".format( switch.getItemName())) offEventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_ON, eventInfo.getItem(), eventInfo.getZone(), eventInfo.getZoneManager(), eventInfo.getEventDispatcher()) TurnOffAdjacentZones().onAction(offEventInfo) return isProcessed
def testOnAction_wrongEventType_returnsFalse(self): (porch, greatRoom, zm, _) = self.createTestData() eventInfo = EventInfo(ZoneEvent.CONTACT_OPEN, ITEMS[0], porch, zm, self.getMockedEventDispatcher()) value = self.action.onAction(eventInfo) self.assertFalse(value)
def testOnAction_zoneDoesNotContainSensor_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.GAS_TRIGGER_STATE_CHANGED, ITEMS[0], Zone('innerZone'), None, self.getMockedEventDispatcher()) value = self.action.onAction(eventInfo) self.assertFalse(value)
def testOnAction_motionEventOnInternalZone_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.MOTION, ITEMS[0], Zone('porch'), None, self.getMockedEventDispatcher()) value = self.action.onAction(eventInfo) self.assertFalse(value)
def testOnAction_noAudioSink_returnsFalse(self): eventInfo = EventInfo(ZoneEvent.SWITCH_TURNED_ON, ITEMS[0], Zone('innerZone'), MockedZoneManager([]), scope.events) value = self.action.onAction(eventInfo) self.assertFalse(value)