# Path: controller/actor.py
# class PoupoolActor(pykka.ThreadingActor):
# 
#     def __init__(self):
#         super().__init__()
#         self._proxy = self.actor_ref.proxy()
#         self.__timer = None
# 
#     def on_failure(self, exception_type, exception_value, traceback):
#         # The actor is going to die
#         logger.fatal(exception_type, exception_value, traceback)
# 
#     def on_stop(self):
#         self.do_cancel()
# 
#     def get_actor(self, name):
#         fsm = pykka.ActorRegistry.get_by_class_name(name)
#         if fsm:
#             return fsm[0].proxy()
#         logger.critical("Actor %s not found!!!" % name)
#         return None
# 
#     def __do_cancel(self):
#         if self.__timer:
#             self.__timer.cancel()
#             self.__timer = None
# 
#     def do_cancel(self):
#         self.__do_cancel()
# 
#     def do_delay(self, delay, method, *args, **kwargs):
#         assert type(method) == str
#         assert delay >= 0
#         # Stop an already running timer
#         self.__do_cancel()
#         func = getattr(self._proxy, method)
#         if delay > 0:
#             self.__timer = Timer(delay, func.defer, *args, **kwargs)
#             self.__timer.start()
#         else:
#             func.defer(*args, **kwargs)
# 
# Path: controller/actor.py
# class PoupoolModel(Machine):
# 
#     def __init__(self, *args, **kwargs):
#         kwargs.setdefault("before_state_change", []).extend(["do_cancel", self.__update_state_time])
#         super().__init__(
#             auto_transitions=False,
#             ignore_invalid_triggers=True,
#             *args,
#             **kwargs
#         )
#         self.__state_time = None
# 
#     def __update_state_time(self):
#         self.__state_time = datetime.now()
# 
#     def get_time_in_state(self):
#         return datetime.now() - self.__state_time
# 
# Path: controller/actor.py
# def do_repeat():
#     def wrap(func):
#         @functools.wraps(func)
#         def wrapped_func(self, *args, **kwargs):
#             try:
#                 func(self, *args, **kwargs)
#             except StopRepeatException:
#                 pass
#             else:
#                 method = re.sub("on_enter_", "do_repeat_", func.__name__)
#                 function = getattr(self._proxy, method)
#                 function.defer()
#         return wrapped_func
#     return wrap
# 
# Path: controller/config.py
# class Config:
#     def __init__(self, config_files):
#     def __getitem__(self, pair):
# def as_list(value, type_cast=int):
# 
# Path: controller/util.py
# class Duration(object):
# 
#     def __init__(self, name):
#         self.__name = name
#         self.__total_duration = timedelta()
#         self.__start = datetime.now()
#         self.__callback = None
# 
#     def set_callback(self, callback):
#         self.__callback = callback
# 
#     def init(self, value):
#         assert isinstance(value, timedelta)
#         self.__total_duration = value
# 
#     @property
#     def duration(self):
#         return self.__total_duration
# 
#     def start(self, value=None):
#         value = value or datetime.now()
#         assert isinstance(value, datetime)
#         self.__start = value
# 
#     def stop(self, value=None):
#         value = value or datetime.now()
#         assert isinstance(value, datetime)
#         diff = value - self.__start
#         assert diff > timedelta()
#         self.__total_duration += diff
#         if self.__callback:
#             self.__callback(self.__total_duration)


import logging
from datetime import datetime, timedelta
from .actor import PoupoolActor
from .actor import PoupoolModel
from .actor import do_repeat
from .config import config
from .util import Duration
        logger.info("Entering halt state")
        self.__heater.off()

    @do_repeat()
    def on_enter_waiting(self):
        logger.info("Entering waiting state")

    def do_repeat_waiting(self):
        temp = self.__read_temperature()
        if temp is None or temp < self.__setpoint - Heater.HYSTERESIS_DOWN:
            self._proxy.heat.defer()
        else:
            self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__)

    @do_repeat()
    def on_enter_heating(self):
        logger.info("Entering heating state")
        self.__heater.on()

    def on_exit_heating(self):
        self.__heater.off()

    def do_repeat_heating(self):
        temp = self.__read_temperature()
        if temp is not None and temp > self.__setpoint + Heater.HYSTERESIS_UP:
            self._proxy.wait.defer()
        else:
            self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_heating.__name__)



# Path: controller/actor.py
# class PoupoolActor(pykka.ThreadingActor):
# 
#     def __init__(self):
#         super().__init__()
#         self._proxy = self.actor_ref.proxy()
#         self.__timer = None
# 
#     def on_failure(self, exception_type, exception_value, traceback):
#         # The actor is going to die
#         logger.fatal(exception_type, exception_value, traceback)
# 
#     def on_stop(self):
#         self.do_cancel()
# 
#     def get_actor(self, name):
#         fsm = pykka.ActorRegistry.get_by_class_name(name)
#         if fsm:
#             return fsm[0].proxy()
#         logger.critical("Actor %s not found!!!" % name)
#         return None
# 
#     def __do_cancel(self):
#         if self.__timer:
#             self.__timer.cancel()
#             self.__timer = None
# 
#     def do_cancel(self):
#         self.__do_cancel()
# 
#     def do_delay(self, delay, method, *args, **kwargs):
#         assert type(method) == str
#         assert delay >= 0
#         # Stop an already running timer
#         self.__do_cancel()
#         func = getattr(self._proxy, method)
#         if delay > 0:
#             self.__timer = Timer(delay, func.defer, *args, **kwargs)
#             self.__timer.start()
#         else:
#             func.defer(*args, **kwargs)
# 
# Path: controller/actor.py
# class PoupoolModel(Machine):
# 
#     def __init__(self, *args, **kwargs):
#         kwargs.setdefault("before_state_change", []).extend(["do_cancel", self.__update_state_time])
#         super().__init__(
#             auto_transitions=False,
#             ignore_invalid_triggers=True,
#             *args,
#             **kwargs
#         )
#         self.__state_time = None
# 
#     def __update_state_time(self):
#         self.__state_time = datetime.now()
# 
#     def get_time_in_state(self):
#         return datetime.now() - self.__state_time
# 
# Path: controller/actor.py
# def do_repeat():
#     def wrap(func):
#         @functools.wraps(func)
#         def wrapped_func(self, *args, **kwargs):
#             try:
#                 func(self, *args, **kwargs)
#             except StopRepeatException:
#                 pass
#             else:
#                 method = re.sub("on_enter_", "do_repeat_", func.__name__)
#                 function = getattr(self._proxy, method)
#                 function.defer()
#         return wrapped_func
#     return wrap
# 
# Path: controller/config.py
# class Config:
#     def __init__(self, config_files):
#     def __getitem__(self, pair):
# def as_list(value, type_cast=int):
# 
# Path: controller/util.py
# class Duration(object):
# 
#     def __init__(self, name):
#         self.__name = name
#         self.__total_duration = timedelta()
#         self.__start = datetime.now()
#         self.__callback = None
# 
#     def set_callback(self, callback):
#         self.__callback = callback
# 
#     def init(self, value):
#         assert isinstance(value, timedelta)
#         self.__total_duration = value
# 
#     @property
#     def duration(self):
#         return self.__total_duration
# 
#     def start(self, value=None):
#         value = value or datetime.now()
#         assert isinstance(value, datetime)
#         self.__start = value
# 
#     def stop(self, value=None):
#         value = value or datetime.now()
#         assert isinstance(value, datetime)
#         diff = value - self.__start
#         assert diff > timedelta()
#         self.__total_duration += diff
#         if self.__callback:
#             self.__callback(self.__total_duration)


# Path: controller/heating.py
import logging
from datetime import datetime, timedelta
from .actor import PoupoolActor
from .actor import PoupoolModel
from .actor import do_repeat
from .config import config
from .util import Duration

        logger.info("Entering halt state")
        self.__heater.off()

    @do_repeat()
    def on_enter_waiting(self):
        logger.info("Entering waiting state")

    def do_repeat_waiting(self):
        temp = self.__read_temperature()
        if temp is None or temp < self.__setpoint - Heater.HYSTERESIS_DOWN:
            self._proxy.heat.defer()
        else:
            self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__)

    @do_repeat()
    def on_enter_heating(self):
        logger.info("Entering heating state")
        self.__heater.on()

    def on_exit_heating(self):
        self.__heater.off()

    def do_repeat_heating(self):
        temp = self.__read_temperature()
        if temp is not None and temp > self.__setpoint + Heater.HYSTERESIS_UP:
            self._proxy.wait.defer()
        else:
            self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_heating.__name__)



class Heating(PoupoolActor):