def initialize(self):
        self.vibration = self.args['vibration_sensor']
        self.door = self.args['open_sensor']

        machine = Machine(self, States)

        machine.add_transitions(IDLE,
                                StateOn(self.vibration),
                                CLEANING,
                                on_transition=self.cleaning)
        machine.add_transition(CLEANING,
                               StateOff(self.vibration),
                               CLEAN,
                               on_transition=self.clean)
        machine.add_transition(CLEAN,
                               Timeout(86400),
                               MUSTY,
                               on_transition=self.musty)
        machine.add_transition(CLEAN,
                               StateOn(self.door),
                               IDLE,
                               on_transition=self.idle)
        machine.add_transition(MUSTY,
                               StateOn(self.door),
                               IDLE,
                               on_transition=self.idle)

        machine.log_graph_link()
class MachineTest(TestCase):

  def setUp(self):
    self.hass = FakeHass()
    self.hass.set_state('sensor.s', 'off')
    self.hass.set_state('sensor.t', 'value1')
    self.machine = Machine(self.hass, States)

  def test_first_state_is_initial(self):
    self.assertEqual(self.machine.current_state, A)

  def test_explicit_initial_state(self):
    machine = Machine(self.hass, States, initial=B)
    self.assertEqual(machine.current_state, B)

  def test_boolean_entity_triggers(self):
    self.machine.add_transition(A, StateOn('sensor.s'), B)
    self.machine.add_transition(B, StateOff('sensor.s'), A)
    self.assertEqual(self.machine.current_state, A)

    self.hass.set_state('sensor.s', 'on')
    self.assertEqual(self.machine.current_state, B)

    self.hass.set_state('sensor.s', 'off')
    self.assertEqual(self.machine.current_state, A)

  def test_valued_entity_triggers(self):
    self.machine.add_transition(A, StateEq('sensor.t', 'value2'), B)
    self.machine.add_transition(B, StateNeq('sensor.t', 'value2'), A)

    self.assertEqual(self.machine.current_state, A)

    self.hass.set_state('sensor.t', 'value2')
    self.assertEqual(self.machine.current_state, B)

    self.hass.set_state('sensor.t', 'value1')
    self.assertEqual(self.machine.current_state, A)

  def test_timeout_trigger(self):
    self.machine.add_transition(A, Timeout(10), B)
    self.machine.add_transition(B, Timeout(20), A)

    self.hass.advance_time(9)
    self.assertEqual(self.machine.current_state, A)
    self.hass.advance_time(1)
    self.assertEqual(self.machine.current_state, B)
    self.hass.advance_time(19)
    self.assertEqual(self.machine.current_state, B)
    self.hass.advance_time(1)
    self.assertEqual(self.machine.current_state, A)

  def test_transitions_cancels_timeout(self):
    self.machine.add_transition(A, StateOn('sensor.s'), B)
    self.machine.add_transitions(A, Timeout(10), C)

    self.hass.set_state('sensor.s', 'on')
    self.assertEqual(self.machine.current_state, B)

    self.hass.advance_time(10)
    self.assertEqual(self.machine.current_state, B)

  def test_transition_to_self_restarts_timer(self):
    self.machine.add_transition(A, StateOn('sensor.s'), A)
    self.machine.add_transitions(A, Timeout(10), B)

    self.hass.advance_time(5)
    self.hass.set_state('sensor.s', 'on')
    self.assertEqual(self.machine.current_state, A)
    self.hass.advance_time(5)
    self.assertEqual(self.machine.current_state, A)
    self.hass.advance_time(5)
    self.assertEqual(self.machine.current_state, B)

  def test_state_entity(self):
    machine = Machine(self.hass, States, entity='sensor.state')
    machine.add_transition(A, Timeout(10), B)

    self.assertEqual(self.hass.get_state('sensor.state'), 'A')
    self.hass.advance_time(10)
    self.assertEqual(self.hass.get_state('sensor.state'), 'B')

  def test_initial_state_from_hass(self):
    self.hass.set_state('sensor.state', 'B')
    machine = Machine(self.hass, States, entity='sensor.state')
    self.assertEqual(machine.current_state, B)

  def test_setting_state_from_hass(self):
    machine = Machine(self.hass, States, entity='sensor.state')
    self.assertEqual(machine.current_state, A)
    self.hass.set_state('sensor.state', 'B')
    self.assertEqual(machine.current_state, B)

  def test_from_any_state(self):
    trigger = Timeout(1)
    with patch.object(self.machine, 'add_transition') as add_transition:
      self.machine.add_transitions(ANY, trigger, A)

    add_transition.assert_any_call(A, trigger, A, None)
    add_transition.assert_any_call(B, trigger, A, None)
    add_transition.assert_any_call(C, trigger, A, None)
    self.assertEqual(add_transition.call_count, 3)

  def test_from_any_transition(self):
    self.machine.add_transitions(ANY, StateOn('sensor.s'), B)

    self.hass.set_state('sensor.s', 'on')
    self.assertEqual(self.machine.current_state, B)

  def test_from_state_list(self):
    trigger = Timeout(1)
    with patch.object(self.machine, 'add_transition') as add_transition:
      self.machine.add_transitions([A, B], trigger, C)

    add_transition.assert_any_call(A, trigger, C, None)
    add_transition.assert_any_call(B, trigger, C, None)
    self.assertEqual(add_transition.call_count, 2)

  def test_trigger_list(self):
    trigger1 = StateOn('sensor.s')
    trigger2 = StateOn('sensor.t')
    with patch.object(self.machine, 'add_transition') as add_transition:
      self.machine.add_transitions(A, [trigger1, trigger2], B)

    add_transition.assert_any_call(A, trigger1, B, None)
    add_transition.assert_any_call(A, trigger2, B, None)
    self.assertEqual(add_transition.call_count, 2)

  def test_state_and_trigger_list(self):
    trigger1 = StateOn('sensor.s')
    trigger2 = StateOn('sensor.t')
    with patch.object(self.machine, 'add_transition') as add_transition:
      self.machine.add_transitions([A, B], [trigger1, trigger2], C)

    add_transition.assert_any_call(A, trigger1, C, None)
    add_transition.assert_any_call(B, trigger1, C, None)
    add_transition.assert_any_call(A, trigger2, C, None)
    add_transition.assert_any_call(B, trigger2, C, None)
    self.assertEqual(add_transition.call_count, 4)

  def test_one_transition_callback(self):
    callback = Mock()
    self.machine.add_transition(A, StateOn('sensor.s'), B, callback)

    self.hass.set_state('sensor.s', 'on')
    callback.assert_called_once_with()

  def test_any_transition_callback(self):
    self.machine.add_transition(A, StateOn('sensor.s'), B)
    callback = Mock()
    self.machine.on_transition(callback)

    self.hass.set_state('sensor.s', 'on')
    callback.assert_called_once_with(A, B)

  def test_immediate_transition(self):
    """If a trigger condition is already met when entering a new state,
    immediately perform the transition."""
    self.machine.add_transition(A, StateOn('sensor.s'), B)
    self.machine.add_transition(B, StateEq('sensor.t', 'value1'), C)

    self.hass.set_state('sensor.s', 'on')
    self.assertEqual(self.machine.current_state, C)

  def test_initial_immediate_transition(self):
    """If a trigger condition is already met when entering a new state,
    immediately perform the transition."""
    self.machine.add_transition(A, StateOff('sensor.s'), B)
    self.assertEqual(self.machine.current_state, B)

  def test_lambda_state_trigger(self):
    self.machine.add_transition(
        A, StateIs('sensor.i', lambda v: int(v) > 5), B)
    self.assertEqual(self.machine.current_state, A)

    self.hass.set_state('sensor.i', '3')
    self.assertEqual(self.machine.current_state, A)

    self.hass.set_state('sensor.i', '6')
    self.assertEqual(self.machine.current_state, B)