Exemplo n.º 1
0
 def call(self, action_call, action_data={}):
     """ Call an action from the registry 
         Format: {namespace}.{action} or 
                 {namespace}.{component}.{action}
     """
     command = self.parse_call(action_call)
     action = self._registry.get(command['namespace'],
                                 {}).get(command['action'])
     if not action:
         # raise MudPiError("Call to action that doesn't exists!")
         Logger.log(
             LOG_LEVEL["error"],
             f'{FONT_YELLOW}Call to action {action_call} that doesn\'t exists!.{FONT_RESET}'
         )
     validated_data = action.validate(action_data)
     if not validated_data and action_data:
         # raise MudPiError("Action data was not valid!")
         Logger.log(
             LOG_LEVEL["error"],
             f'{FONT_YELLOW}Action data was not valid for {action_call}{FONT_RESET}'
         )
     self.mudpi.events.publish(
         'core', {
             'event': 'ActionCall',
             'action': action_call,
             'data': action_data,
             'namespace': command['namespace']
         })
     action(data=validated_data)
Exemplo n.º 2
0
    def check_time(self):
        """ Checks the time to see if it is currently sunrise or sunset """

        # Get state object from manager
        state = self.mudpi.states.get(self.source)

        if state is not None:
            _state = state.state
        else:
            _state = None

        if _state:
            try:
                _value = self._parse_data(_state)
                _now = datetime.datetime.now().replace(microsecond=0)
                if _value:
                    _value = datetime.datetime.strptime(_value, "%Y-%m-%d %I:%M:%S %p").replace(microsecond=0) + self.offset
                if _now == _value:
                    self.active = True
                    if self._previous_state != self.active:
                        # Trigger is reset, Fire
                        self.trigger(_value.strftime('%Y-%m-%d %I:%M:%S %p'))
                    else:
                        # Trigger not reset check if its multi fire
                        if self.frequency == 'many':
                            self.trigger(_value.strftime('%Y-%m-%d %I:%M:%S %p'))
                else:
                    self.active = False
            except Exception as error:
                Logger.log(LOG_LEVEL["error"],
                           f'Error evaluating thresholds for trigger {self.id}')
                Logger.log(LOG_LEVEL["debug"], error)
        self._previous_state = self.active
Exemplo n.º 3
0
 def update(self):
     """ Get data from GPIO through nanpy"""
     if self.node.connected:
         self.check_connection()
         try:
             data = self.node.api.analogRead(
                 self.pin) if self.analog else self.node.api.digitalRead(
                     self.pin)
             if self.type != 'potentiometer':
                 self.previous_state = self._state
                 self._state = data
             else:
                 if (data < self.previous_state - self.buffer) or (
                         data > self.previous_state + self.buffer):
                     self.previous_state = self._state
                     self._state = data
             self.handle_state()
         except (SerialManagerError, SocketManagerError, BrokenPipeError,
                 ConnectionResetError, OSError, socket.timeout) as e:
             if self.node.connected:
                 Logger.log_formatted(
                     LOG_LEVEL["warning"],
                     f'{self.node.key} -> Broken Connection', 'Timeout',
                     'notice')
                 self.node.reset_connection()
             self._pin_setup = False
     else:
         self._pin_setup = False
     return None
Exemplo n.º 4
0
 def update(self):
     """ Get data from DHT through nanpy"""
     if self.node.connected:
         try:
             self.check_connection()
             if self._dht:
                 _temp_format = self.mudpi.unit_system == IMPERIAL_SYSTEM
                 temperature = self._dht.readTemperature(_temp_format)
                 humidity = self._dht.readHumidity()
                 data = {
                     'temperature': round(temperature, 2),
                     'humidity': round(humidity, 2)
                 }
                 self._state = data
         except (SerialManagerError, SocketManagerError, BrokenPipeError,
                 ConnectionResetError, OSError, socket.timeout) as e:
             if self.node.connected:
                 Logger.log_formatted(
                     LOG_LEVEL["warning"],
                     f'{self.node.key} -> Broken Connection', 'Timeout',
                     'notice')
                 self.node.reset_connection()
             self._dht = None
     else:
         self._dht = False
     return None
Exemplo n.º 5
0
    def update(self):
        """ Get data from DHT device"""
        humidity = None
        temperature_c = None

        if self.check_dht():
            try:
                humidity, temperature_c = Adafruit_DHT.read_retry(
                    self._sensor, self.pin)
            except Exception as error:
                # Errors happen fairly often, DHT's are hard to read
                Logger.log(LOG_LEVEL["debug"], error)

            if humidity is not None and temperature_c is not None:
                _temperature = temperature_c if self.mudpi.unit_system == METRIC_SYSTEM else (
                    temperature_c * 1.8 + 32)
                readings = {
                    'temperature': round(_temperature, 2),
                    'humidity': round(humidity, 2)
                }
                self._state = readings
                return readings
            else:
                Logger.log(LOG_LEVEL["debug"],
                           f'DHT Reading was Invalid (Legacy).')
            time.sleep(2.1)
        return None
Exemplo n.º 6
0
    def next_step(self, event_data=None):
        """ Advance to the next sequnce step
            Makes sure any delays and durations are done """

        # Step must be flagged complete to advance
        if self._step_complete:
            if self.active:
                # If skipping steps trigger unperformed actions
                if not self._step_triggered:
                    if self.evaluate_thresholds():
                        self.trigger()
                # Sequence is already active, advance to next step
                if self._current_step < self.total_steps - 1:
                    self.reset_step()
                    self._current_step += 1
                    self.fire({"event": "SequenceStepStarted"})
                    Logger.log_formatted(
                        LOG_LEVEL["info"],
                        f'Sequence: {FONT_CYAN}{self.name}{FONT_RESET}', f'Step {self._current_step+1}/{self.total_steps}'
                    )
                else:
                    # Last step of sequence completed
                    self.active = False
                    self.fire({"event": "SequenceEnded"})
                    Logger.log_formatted(
                        LOG_LEVEL["info"],
                        f'Sequence {FONT_CYAN}{self.name}{FONT_RESET}', 'Completed', 'success'
                    )
                self.reset_duration()
Exemplo n.º 7
0
    def update(self):
        """ Get data from T9602 device"""
        for trynb in range(5):  # 5 tries
            try:
                data = self.bus.read_i2c_block_data(self.config['address'], 0, 4)
                break
            except OSError:
                Logger.log(
                    LOG_LEVEL["info"],
                    "Single reading error [t9602]. It happens, let's try again..."
                )
                time.sleep(2)


        humidity = (((data[0] & 0x3F) << 8) + data[1]) / 16384.0 * 100.0
        temperature_c = ((data[2] * 64) + (data[3] >> 2)) / 16384.0 * 165.0 - 40.0

        humidity = round(humidity, 2)
        temperature_c = round(temperature_c, 2)

        if humidity is not None and temperature_c is not None:
            _temperature = temperature_c if self.mudpi.unit_system == METRIC_SYSTEM else (temperature_c * 1.8 + 32)
            readings = {
                'temperature': _temperature,
                'humidity': humidity
            }
            self._state = readings
            return readings
        else:
            Logger.log(
                LOG_LEVEL["error"],
                'Failed to get reading [t9602]. Try again!'
            )
        return None
Exemplo n.º 8
0
    def create(cls, mudpi, extension_name, extensions_module):
        """ Static method to load extension """
        for path in extensions_module.__path__:
            config_path = os.path.join(path, extension_name, "extension.json")

            if not os.path.isfile(config_path):
                continue

            try:
                with open(config_path) as f:
                    config = json.loads(f.read())
            except FileNotFoundError:
                Logger.log(
                    LOG_LEVEL["error"],
                    f'{FONT_RED}No extension.json found at {config_path}.{FONT_RESET}'
                )
                continue
            except Exception as e:
                Logger.log(
                    LOG_LEVEL["error"],
                    f'{FONT_RED}Error loading extension.json at {config_path} {error}.{FONT_RESET}'
                )
                continue

            return cls(mudpi, config,
                       f"{extensions_module.__name__}.{extension_name}",
                       os.path.split(config_path)[0])

        return None
Exemplo n.º 9
0
    def handle_event(self, event):
        """ Handle events from event system """
        _event = None
        try: 
            _event = decode_event_data(event)
        except Exception as error:
            _event = decode_event_data(event['data'])

        if _event == self._last_event:
            # Event already handled
            return

        self._last_event = _event
        if _event is not None:
            try:
                if _event['event'] == 'Message':
                    if _event.get('data', None):
                        self.add_message(_event['data'])
                elif _event['event'] == 'Clear':
                    self.clear()
                elif _event['event'] == 'ClearQueue':
                    self.clear_queue()
            except Exception as error:
                Logger.log(LOG_LEVEL["error"],
                           f'Error handling event for {self.id}')
Exemplo n.º 10
0
    def handle_event(self, event):
        """ Handle the event data from the event system """
        _event_data = decode_event_data(event)

        if _event_data == self._last_event:
            # Event already handled
            return

        self._last_event = _event_data
        if _event_data.get('event'):
            try:
                if _event_data['event'] == 'StateUpdated':
                    if _event_data['component_id'] == self.source:
                        sensor_value = self._parse_data(
                            _event_data["new_state"]["state"])
                        if self.evaluate_thresholds(sensor_value):
                            self.active = True
                            if self._previous_state != self.active:
                                # Trigger is reset, Fire
                                self.trigger(_event_data)
                            else:
                                # Trigger not reset check if its multi fire
                                if self.frequency == 'many':
                                    self.trigger(_event_data)
                        else:
                            self.active = False
            except Exception as error:
                Logger.log(
                    LOG_LEVEL["error"],
                    f'Error evaluating thresholds for trigger {self.id}')
                Logger.log(LOG_LEVEL["debug"], error)
        self._previous_state = self.active
Exemplo n.º 11
0
 def stop(self, data=None):
     """ Stop the timer """
     if self.active:
         self._active = False
         self.reset()
         Logger.log(
             LOG_LEVEL["debug"],
             f'Timer Sensor {FONT_MAGENTA}{self.name}{FONT_RESET} Stopped')
Exemplo n.º 12
0
    def update(self):
        """ Main run loop for sequence to check
            time past and if it should fire actions """
        if self.mudpi.is_prepared:
            try:
                if self.active:
                    if not self._step_complete:
                        if not self._delay_complete:
                            if self.step_delay is not None:
                                if self.duration > self.step_delay:
                                    self._delay_complete = True
                                    self._delay_actual = self.duration
                                    self.reset_duration()
                                else:
                                    # Waiting break early
                                    return
                            else:
                                self._delay_complete = True
                                self.reset_duration()

                        if self._delay_complete:
                            if not self._step_triggered:
                                if self.evaluate_thresholds():
                                    self.trigger()
                                else:
                                    if self.current_step.get('thresholds') is not None:
                                        # Thresholds failed skip step without trigger
                                        self._step_triggered = True
                                        self._step_complete = True

                        if self.step_duration is not None and not self._step_complete:
                            if self.duration > self.step_duration:
                                self._step_complete = True
                                self._duration_actual = self.duration
                                self.reset_duration()
                            else:
                                # Waiting break early
                                return
                        else:
                            # No duration set meaning step only advances
                            # manualy by calling actions and events. RTM
                            pass

                    if self._step_complete:
                        self.fire({"event": "SequenceStepEnded"})
                        # Logger.log(
                        #     LOG_LEVEL["debug"],
                        #     f'Sequence {FONT_CYAN}{self.name}{FONT_RESET} Step {self._current_step+1} Debug\n' \
                        #     f'Delay: {self.step_delay} Actual: {self._delay_actual} Duration: {self.step_duration} Actual: {self._duration_actual}'
                        # )
                        return self.next_step()
                else:
                    # Sequence is not active.
                    self.reset_duration()
            except Exception as e:
                    Logger.log_formatted(LOG_LEVEL["error"],
                               f'Sequence {self.id}', 'Unexpected Error', 'error')
                    Logger.log(LOG_LEVEL["critical"], e)
Exemplo n.º 13
0
 def handle_event(self, data={}):
     """ Handle event from mqtt broker """
     if data is not None:
         try:
             # _event_data = self.last_event = decode_event_data(data)
             self._state = data
         except:
             Logger.log(LOG_LEVEL["info"],
                        f"Error Decoding Event for MQTT Sensor {self.id}")
Exemplo n.º 14
0
 def run(self, func=None):
     """ Create a thread and return it """
     if not self._thread:
         self._thread = threading.Thread(target=self.work, args=(func, ))
         Logger.log_formatted(LOG_LEVEL["debug"], f"Worker {self.key} ",
                              "Starting", "notice")
         self._thread.start()
         Logger.log_formatted(LOG_LEVEL["info"], f"Worker {self.key} ",
                              "Started", "success")
     return self._thread
Exemplo n.º 15
0
 def reset(self, event_data=None):
     """ Reset the entire sequence """
     self._current_step = 0
     self.reset_step()
     self.fire({
         "event": "SequenceReset"
     })
     Logger.log_formatted(
         LOG_LEVEL["info"],
         f'Sequence {FONT_CYAN}{self.name}{FONT_RESET}', 'Reset', 'warning'
     )
Exemplo n.º 16
0
 def restore_states(self):
     """ Restore previous components states on first boot """
     _comp_ids = self.components.ids()
     for state_id in self.states.ids():
         if state_id in _comp_ids:
             comp = self.components.get(state_id)
             comp.restore_state(self.states.get(state_id))
             Logger.log(LOG_LEVEL["debug"],
                        f"Restored State for {state_id}")
         else:
             self.states.remove(state_id)
Exemplo n.º 17
0
 def import_config_dir(self):
     """ Add config dir to sys path so we can import extensions """
     if self.mudpi.config_path is None:
         Logger.log(
             LOG_LEVEL["error"],
             f'{RED_BACK}Could not import config_path - No path was set.{FONT_RESET}'
         )
         return False
     if self.mudpi.config_path not in sys.path:
         sys.path.insert(0, self.mudpi.config_path)
     return True
Exemplo n.º 18
0
 def connect(self, connection):
     """ Connect the sensor to redis """
     self.conn = connection
     if self.type in ("current", "forecast"):
         self.sensor = "https://api.openweathermap.org/data/2.5/onecall?exclude=minutely&%s" % (
             str(self.conn))
     elif self.type in ("historical"):
         self.sensor = "https://api.openweathermap.org/data/2.5/onecall/timemachine?%s&dt=" % (
             str(self.conn))
     Logger.log(LOG_LEVEL["debug"],
                'OwmapiSensor: apicall: ' + str(self.sensor))
Exemplo n.º 19
0
 def handle_event(self, event={}):
     """ Handle event from redis pubsub """
     data = decode_event_data(event['data'])
     if data is not None:
         try:
             # _event_data = self.last_event = decode_event_data(data)
             self._state = data
         except:
             Logger.log(
                 LOG_LEVEL["info"],
                 f"Error Decoding Event for Redis Sensor {self.id}"
             )
Exemplo n.º 20
0
 def connect(self):
     """ Setup connections for all adaptors """
     connection_data = {}
     for key, adaptor in self.adaptors.items():
         Logger.log_formatted(LOG_LEVEL["debug"],
                              f"Preparing Event System for {key} ",
                              'Pending', 'notice')
         connection_data[key] = adaptor.connect()
         Logger.log_formatted(LOG_LEVEL["info"],
                              f"Event System Ready on {key}  ", 'Connected',
                              'success')
     return connection_data
Exemplo n.º 21
0
def _install_extension_requirements(mudpi, extension):
    """ Installs all the extension requirements """
    cache = mudpi.cache.setdefault('extensions_requirements_installed', {})

    if cache.get(extension.namespace) is not None:
        # Already processed and installed
        return cache[extension.namespace]

    # Handle all the dependencies requirements
    if extension.has_dependencies:
        if extension.import_dependencies():
            for dependency in extension.loaded_dependencies:
                try:
                    dependency_extension = get_extension_importer(
                        mudpi, dependency)
                except Exception as error:
                    Logger.log(
                        LOG_LEVEL["error"],
                        f'Error getting extension <{extension}> dependency: {FONT_YELLOW}{dependency}{FONT_RESET}'
                    )
                if not dependency_extension.install_requirements():
                    Logger.log(
                        LOG_LEVEL["error"],
                        f'Error with extension <{extension}> dependency: {FONT_YELLOW}{dependency}{FONT_RESET} requirements.'
                    )

    if not extension.has_requirements:
        cache[extension.namespace] = extension
        return cache[extension.namespace]

    requirement_cache = mudpi.cache.setdefault('requirement_installed', {})
    for requirement in extension.requirements:
        if requirement not in requirement_cache:
            if not utils.is_package_installed(requirement):
                Logger.log_formatted(
                    LOG_LEVEL["info"],
                    f'{FONT_YELLOW}{extension.namespace.title()}{FONT_RESET} requirements',
                    'Installing', 'notice')
                Logger.log(
                    LOG_LEVEL["debug"],
                    f'Installing package {FONT_YELLOW}{requirement}{FONT_RESET}',
                )
                if not utils.install_package(requirement):
                    Logger.log(
                        LOG_LEVEL["error"],
                        f'Error installing <{extension.title()}> requirement: {FONT_YELLOW}{requirement}{FONT_RESET}'
                    )
                    return False
                requirement_cache[requirement] = True
    # extension.requirements_installed = True
    cache[extension.namespace] = extension
    return cache[extension.namespace]
Exemplo n.º 22
0
 def start(self, event_data=None):
     """ Start the sequence """
     if not self.active:
         self._current_step = 0
         self.active = True
         self.reset_step()
         self.fire({
             "event": "SequenceStarted"
         })
         Logger.log_formatted(
             LOG_LEVEL["info"],
             f'Sequence {FONT_CYAN}{self.name}{FONT_RESET}', 'Started', 'success'
         )
Exemplo n.º 23
0
 def stop(self, event_data=None):
     """ Stop the sequence """
     if self.active:
         self._current_step = 0
         self.active = False
         self.reset_step()
         self.fire({
             "event": "SequenceStopped"
         })
         Logger.log_formatted(
             LOG_LEVEL["info"],
             f'Sequence {FONT_CYAN}{self.name}{FONT_RESET}', 'Stopped', 'error'
         )
Exemplo n.º 24
0
    def validate(self, config):
        """ Validate the trigger config """
        if not isinstance(config, list):
            config = [config]

        for conf in config:
            if not conf.get('schedule'):
                Logger.log(
                    LOG_LEVEL["debug"],
                    'Trigger: No `schedule`, defaulting to every 5 mins')
                # raise ConfigError('Missing `schedule` in Trigger config.')

        return config
Exemplo n.º 25
0
 def check_dht(self):
     """ Check if the DHT device is setup """
     if self._sensor is None:
         try:
             self._sensor = self._dht_device
         except Exception as error:
             Logger.log(
                 LOG_LEVEL["error"],
                 'Sensor Initialize Error: DHT (Legacy) Failed to Init')
             self._sensor = None
             Logger.log(LOG_LEVEL["debug"], error)
             return False
     return True
Exemplo n.º 26
0
    def init(self):
        """ Setup the socket """
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client_threads = []
        self._server_ready = threading.Event()
        self._server_ready.set()
        self._server_running = False

        try:
            self.sock.bind((self.host, self.port))
        except socket.error as msg:
            Logger.log(LOG_LEVEL['error'], f'Failed to create socket. Error Code: {str(msg[0])} Error Message: {msg[1]}')
            return False
        return True
Exemplo n.º 27
0
def get_extension_importer(mudpi, extension, install_requirements=False):
    """ Find or create an extension importer, Loads it if not loaded, 
        Checks cache first.
        
        Set install_requirements to True to also have all requirements 
        checked through pip.
    """

    # First we check if the namespace is disabled. Could be due to errors or configs
    disabled_cache = mudpi.cache.setdefault("disabled_namespaces", {})
    if extension in disabled_cache:
        raise MudPiError(f"Extension is {extension} is disabled.")

    if install_requirements:
        extension_importer = _extension_with_requirements_installed(
            mudpi, extension)
        if extension_importer is not None:
            return extension_importer

    importer_cache = mudpi.cache.setdefault("extension_importers", {})

    try:
        extension_importer = importer_cache.get(extension)
        if extension_importer is not None:
            return extension_importer
    except Exception as error:
        extension_importer = None

    if extension_importer is None:
        extension_importer = _get_custom_extensions(mudpi).get(extension)
        if extension_importer is not None:
            Logger.log(
                LOG_LEVEL["warning"],
                f'{FONT_YELLOW}You are using {extension} which is not provided by MudPi.{FONT_RESET}\nIf you experience errors, remove it.'
            )
            return extension_importer

    # Component not found look in internal extensions
    from mudpi import extensions

    extension_importer = ExtensionImporter.create(mudpi, extension, extensions)

    if extension_importer is not None:
        importer_cache[extension] = extension_importer
        Logger.log_formatted(
            LOG_LEVEL["info"],
            f'{extension_importer.namespace.title()} Ready for Import',
            'Ready', 'success')
    else:
        Logger.log_formatted(LOG_LEVEL["debug"],
                             f'Import Preperations for {extension.title()}',
                             'error', 'error')
        Logger.log(
            LOG_LEVEL["debug"],
            f'{FONT_YELLOW}`{extension.title()}` was not found.{FONT_RESET}')
        disabled_cache[extension] = 'Not Found'
        raise ExtensionNotFound(extension)

    return extension_importer
Exemplo n.º 28
0
 def check(self):
     """ Check trigger schedule thresholds """
     if self.mudpi.is_running:
         try:
             if pycron.is_now(self.schedule):
                 self.trigger()
                 if not self.active:
                     self.active = True
             else:
                 if self.active:
                     self.active = False
         except Exception as error:
             Logger.log(LOG_LEVEL["error"],
                        "Error evaluating time trigger schedule.")
     return
Exemplo n.º 29
0
    def validate(self, config):
        """ Validate the dht config """
        if not isinstance(config, list):
            config = [config]

        for conf in config:
            if not conf.get('pin'):
                raise ConfigError('Missing `pin` in DHT config.')

            if str(conf.get('model')) not in DHTSensor.models:
                conf['model'] = '11'
                Logger.log(LOG_LEVEL["warning"],
                           'Sensor Model Error: Defaulting to DHT11')

        return config
Exemplo n.º 30
0
 def start(self, data=None):
     """ Start the timer """
     if not self.active:
         self.reset_duration()
         self._active = True
         if self._pause_offset == 0:
             Logger.log(
                 LOG_LEVEL["debug"],
                 f'Timer Sensor {FONT_MAGENTA}{self.name}{FONT_RESET} Started'
             )
         else:
             Logger.log(
                 LOG_LEVEL["debug"],
                 f'Timer Sensor {FONT_MAGENTA}{self.name}{FONT_RESET} Resumed'
             )