Example #1
0
    def test_except_invalid_action_function(self):
        spec = clock.TimeSpec.from_dict({"tz": "UTC"})
        spec_id = uuid.uuid4()

        # None reference should fail the assert
        self.assertRaises(AssertionError,
                          self.clock.add_timespec_action,
                          id=spec_id,
                          action_function=None,
                          timespec=spec,
                          nowtime=nowutc())

        # Non-async function should fail the assert
        def _not_async_function():
            pass

        self.assertRaises(AssertionError,
                          self.clock.add_timespec_action,
                          id=spec_id,
                          action_function=_not_async_function,
                          timespec=spec,
                          nowtime=nowutc())

        # This should pass
        async def _is_async_function():
            pass

        try:
            self.clock.add_timespec_action(id=spec_id,
                                           action_function=_is_async_function,
                                           timespec=spec,
                                           nowtime=nowutc())
        except AssertionError as e:
            self.fail(str(e))
Example #2
0
    def evaluate(self, engine) -> bool:
        now = helpers.nowutc()  # datetime.datetime

        if self._before_offset is None:
            self._before_offset = datetime.timedelta(0)  # datetime.timedelta
        if self._after_offset is None:
            self._after_offset = datetime.timedelta(0)  # datetime.timedelta

        sun_state = engine.states.get_entity_state(self._entity_id)
        next_rising = dateutil.parser.parse(
            sun_state.attributes.get('next_rising'))  # datetime.datetime
        next_setting = dateutil.parser.parse(
            sun_state.attributes.get('next_setting'))  # datetime.datetime

        # False if now is already after the specified time before sunrise
        if self._before == 'sunrise' and now > (next_rising +
                                                self._before_offset):
            return False

        # False if now is already after the specified time before sunset
        elif self._before == 'sunset' and now > (next_setting +
                                                 self._before_offset):
            return False

        # False if now is still before the specified time after sunrise
        if self._after == 'sunrise' and now < (next_rising +
                                               self._after_offset):
            return False

        # False if now is still before the specified timea fter sunset
        elif self._after == 'sunset' and now < (next_setting +
                                                self._after_offset):
            return False

        return True
Example #3
0
    def test_remove_action(self):
        """Tests the removal of a TimeSpec from the timeline
        """
        async def _noop():
            pass

        spec = clock.TimeSpec.from_dict({"tz": "UTC"})
        spec_id = uuid.uuid4()
        self.clock.add_timespec_action(id=spec_id,
                                       action_function=_noop,
                                       timespec=spec,
                                       nowtime=nowutc())

        # Add the TimeSpecAction
        self.assertEqual(len(self.clock.timeline), 1)
        actual_id = self.clock.timeline[0].actions[0].id
        print("Expected: {}, Actual: {}".format(spec_id, actual_id))
        self.assertEqual(actual_id, spec_id)

        # Remove the TimeSpecAction
        self.clock.remove_timespec_action(spec_id)
        print(self.clock._format_timeline())
        self.assertEqual(
            len(self.clock.timeline),
            0,
            msg="Expecing an empty timeline, but it found non-empty")
    def test_action_condition(self):
        rule_id = "action_condition"
        cfg = config.EngineConfig()
        cfg.json_rules_dir = self.test_rules_dir
        self._setup_engine(config_obj=cfg)

        # Set the simulation time
        self.sim_time = helpers.nowutc()

        print("Loading the rule")
        self.loop.run_until_complete(
            self._load_one_rule(rule_id, self.test_rules_dir))

        print("Setting pre-state")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.action_light",
                "off",
                "on",
            ))
        self.engine_obj._websocket.clear()

        print("Triggering rule")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.test",
                "on",
                "off",
            ))

        # Check that the correct service was called
        self._verify_websocket_service_call(0, "input_boolean.action_light",
                                            "turn_on")
        self.engine_obj._websocket.clear()
def event_state_changed(id, entity_id: str, old_state: str, new_state: str):
    now = helpers.nowutc()
    event = {
        "id": id,
        "type": "event",
        "event": {
            "event_type": "state_changed",
            "data": {
                "entity_id": entity_id,
                "old_state": {
                    "entity_id": entity_id,
                    "state": old_state,
                    "attributes": {},
                    "last_changed": now.isoformat(),
                    "last_updated": now.isoformat()
                },
                "new_state": {
                    "entity_id": entity_id,
                    "state": new_state,
                    "attributes": {},
                    "last_changed": now.isoformat(),
                    "last_updated": now.isoformat()
                }
            },
            "origin": "LOCAL",
            "time_fired": now.isoformat()
        }
    }
    return event
Example #6
0
 async def _async_run(self):
     """Override of Fiber base class.  Called by async Fiber.async_run()
     """
     # Start the _tick() loop
     while self._running:
         await self._async_tick(helpers.nowutc())    # Execute tick
         await asyncio.sleep(TICK_INTERVAL_SECONDS)  # Sleep until next tick
Example #7
0
 def add(self, logtype: str, logentry: dict):
     if self._max_logs > 0:
         self._log.append({
             "ts": str(helpers.nowutc()),
             "type": logtype,
             "entry": logentry,
         })
         self._trim_log()
    def test_2764351_away_lights(self):
        rule_id = "2764351"
        cfg = config.EngineConfig()
        cfg.json_rules_dir = self.realworld_rules_dir
        self._setup_engine(config_obj=cfg)

        # Set the simulation time
        self._monkey_patch_nowutc()
        local_tz = pytz.timezone(tz_name)
        self.sim_time = local_tz.localize(dt.datetime(2018, 7, 14, 0, 0, 00))

        print("Loading the rule")
        self.loop.run_until_complete(
            self._load_one_rule(rule_id, self.realworld_rules_dir))

        # Verify rule loaded properly
        print("Clock should have two ClockAlarms defined on the timeline")
        self.assertEqual(len(self.engine_obj._clock.timeline), 2)

        print("Setting the pre-state")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state("input_boolean.vacation_mode",
                                              "on", "off"))

        print("Trigger lights on")
        self.sim_time = local_tz.localize(dt.datetime(2018, 7, 14, 19, 21, 00))
        print("Running sim at: ", str(helpers.nowutc()))
        self.loop.run_until_complete(
            self.engine_obj._clock._async_tick(self.sim_time))

        # Check that the correct service was called
        self._verify_websocket_service_call(0, "scene.living_room_bright",
                                            "turn_on")
        self.engine_obj._websocket.clear()

        print("Trigger lights off")
        self.sim_time = local_tz.localize(dt.datetime(2018, 7, 14, 22, 7, 00))
        print("Running sim at: ", str(helpers.nowutc()))
        self.loop.run_until_complete(
            self.engine_obj._clock._async_tick(self.sim_time))

        # Check that the correct service was called
        self._verify_websocket_service_call(0, "scene.all_lights_off",
                                            "turn_on")
        self.engine_obj._websocket.clear()
Example #9
0
 def check_timespec_threadsafe(self, spec_dict):
     try:
         spec = clock.TimeSpec.from_dict(spec_dict)
         next_time = spec.next_time_from(helpers.nowutc()).isoformat()
     except Exception as e:
         message = "Exception checking TimeSpec: {} ({})".format(
             spec_dict,
             sys.exc_info()[1])
         _LOG.error(message)
         return {"success": False, "message": message}
     return {"success": True, "next_time": next_time}
Example #10
0
def event_hass_event(id, event_type: str, event_data: str):
    now = helpers.nowutc()
    event = {
        "id": id,
        "type": "event",
        "event": {
            "event_type": event_type,
            "data": event_data,
            "origin": "LOCAL",
            "time_fired": now.isoformat()
        }
    }
    return event
Example #11
0
    def __init__(self):
        # This is intentionally named different from OttoEngine's public state attribute
        # to ensure the REST API is not accessing the OttoEngine state directly.
        # The restapi runs in its own thread, and therefore should only be going through
        # threadsafe methods.
        self._hidden_states = state.OttoEngineState()

        self._hidden_states.set_engine_state("start_time", helpers.nowutc())
        
        # Add some rules
        for i in range(1, 6):
            id = str(i)*5
            self._hidden_states.add_rule(
                AutomationRule(id, "Rule {}".format(id), enabled=True, group="unittest")
            )
    def test_disabled_rule(self):
        rule_id = "1116"
        cfg = config.EngineConfig()
        cfg.json_rules_dir = self.test_rules_dir
        self._setup_engine(config_obj=cfg)

        # Set the simulation time
        self._monkey_patch_nowutc()
        local_tz = pytz.timezone(tz_name)
        self.sim_time = local_tz.localize(dt.datetime(2018, 7, 14, 9, 0, 0))

        print("Loading the rule")
        self.loop.run_until_complete(
            self._load_one_rule(rule_id, self.test_rules_dir))

        self.sim_time = local_tz.localize(dt.datetime(2018, 7, 14, 9, 1, 00))
        print("Running sim at: ", str(helpers.nowutc()))
        self.loop.run_until_complete(
            self.engine_obj._clock._async_tick(helpers.nowutc()))

        # Rule should not fire since it's disabled
        # self._verify_websocket_service_call(0, "input_boolean.test", "turn_on")
        self.assertEqual(len(self.engine_obj._websocket.service_calls), 0)
        self.engine_obj._websocket.clear()
Example #13
0
    def start_engine(self):
        '''Starts the Otto Engine until it is shutdown'''

        # Setup signal handlers
        self._loop.add_signal_handler(signal.SIGINT, self._stop_engine)
        self._loop.add_signal_handler(signal.SIGTERM, self._stop_engine)

        # Start the event loop
        try:
            _LOG.info("Starting event loop")
            self.englog.add_event("Otto-Engine starting")
            self._loop.call_soon(self._states.set_engine_state, "start_time",
                                 helpers.nowutc())
            self._loop.create_task(self._async_setup_engine())
            self._loop.run_forever()
        finally:
            _LOG.info("Shutting down OttoEngine")
            self._loop.close()
Example #14
0
    def _load_listeners(self, rule: rule_objects.AutomationRule):

        for listener in rule_objects.get_listeners(rule):

            # State and Event triggers
            if isinstance(listener.trigger, trigger_objects.ListenerTrigger):
                if isinstance(listener.trigger, trigger_objects.EventTrigger):
                    listener_id = listener.trigger.event_type
                elif isinstance(listener.trigger,
                                (trigger_objects.StateTrigger,
                                 trigger_objects.NumericStateTrigger)):
                    listener_id = listener.trigger.entity_id
                _LOG.info("Adding listener for {} (rule: {})".format(
                    listener_id, rule.id))
                if listener_id in self._event_listeners:
                    self._event_listeners[listener_id].append(listener)
                else:
                    self._event_listeners[listener_id] = [listener]

            # Time triggers
            if isinstance(listener.trigger, trigger_objects.TimeTrigger):
                _LOG.info("Adding time listener: (rule: {}) {}".format(
                    listener.rule.id, listener.trigger.timespec.serialize()))

                async def async_time_triggered(engine_obj=self):
                    await async_invoke_rule(engine_obj,
                                            rule,
                                            trigger=None,
                                            event=None),

                self._clock.add_timespec_action(listener.trigger.id,
                                                async_time_triggered,
                                                listener.trigger.timespec,
                                                helpers.nowutc())
                # Add reference so we can find the listener id to remove it
                self._time_listeners.append(listener.trigger.id)
Example #15
0
 def evaluate(self, engine) -> bool:
     return self.evaluate_at(helpers.nowutc())
    def test_rule_condition(self):
        rule_id = "rule_condition"
        cfg = config.EngineConfig()
        cfg.json_rules_dir = self.test_rules_dir
        self._setup_engine(config_obj=cfg)

        # Set the simulation time
        self.sim_time = helpers.nowutc()

        print("Loading the rule")
        self.loop.run_until_complete(
            self._load_one_rule(rule_id, self.test_rules_dir))

        print("Setting pre-state")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.action_light",
                "off",
                "on",
            ))
        self.engine_obj._websocket.clear()

        print("Triggering rule, rule should pass")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.test",
                "on",
                "off",
            ))

        # Check that the correct service was called
        self._verify_websocket_service_call(0, "input_boolean.action_light",
                                            "turn_on")
        self.engine_obj._websocket.clear()

        # Update the light's state, since Home Assistant would respond to the service_call
        # with any state changed events
        self.loop.run_until_complete(
            self._set_and_verify_entity_state("input_boolean.action_light",
                                              "on", None))

        # Now that light is on, triggering input_boolean.test to on again
        # should not run the rule

        print("Turning off input_boolean.test, should not trigger rule")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.test",
                "off",
                "on",
            ))
        self.assertEqual(len(self.engine_obj._websocket.service_calls), 0)

        print("Triggering again, but light is already on, rule should not run")
        self.loop.run_until_complete(
            self._set_and_verify_entity_state(
                "input_boolean.test",
                "on",
                "off",
            ))
        self.assertEqual(len(self.engine_obj._websocket.service_calls), 0)