def __init__(self, settings: Settings): self._components = ComponentRegistry(settings) # thread for watchdog self._watch_thread: Optional[ threading. Thread] = None # re-usable thread, assignment is in init_all self._stop_event = threading.Event() # own stop event for watchdog # thread for waiting for comps unload self._unload_thread: Optional[ threading. Thread] = None # re-usable thread, assignment is in unload_all
def testStandbyInDayMode(base_fixture, target_mockup_fixture): settings = Settings(base_fixture.testdata_path / "integration") settings.set(MOTION_SENSOR_ENABLED, True) settings.set(NIGHT_MODE_BEGIN, 22) settings.set(NIGHT_MODE_END, 5) settings.set(BRIGHTNESS, 70) settings.set(DAY_STANDBY_TIMEOUT, 10) energy_saver = None comps = ComponentRegistry(settings) disp = comps.display # day with freeze_time("2019-01-01 12:59:59"): energy_saver = comps.energy_saver time.sleep(energy_saver.INIT_WAIT_TIME) time.sleep(energy_saver.UPDATE_TIME + 1) assert energy_saver.night_mode_active == False assert disp.get_brightness() == STANDBY_BRIGHTNESS # switch to wake comps.motion_detection_sensor._motion_detected = 1 with freeze_time("2019-01-01 13:00:10"): time.sleep(energy_saver.UPDATE_TIME + 2) assert disp.get_brightness() == settings.get(BRIGHTNESS) time.sleep(10 - energy_saver.UPDATE_TIME) # switch to standby comps.motion_detection_sensor._motion_detected = 0 time.sleep(energy_saver.UPDATE_TIME * 2) assert disp.get_brightness() == STANDBY_BRIGHTNESS energy_saver.stop()
def testNightModeEnter(base_fixture, target_mockup_fixture): settings = Settings(base_fixture.testdata_path / "integration") settings.set(MOTION_SENSOR_ENABLED, True) settings.set(NIGHT_MODE_BEGIN, 23) settings.set(NIGHT_MODE_END, 5) settings.set(BRIGHTNESS, 70) energy_saver = None comps = ComponentRegistry(settings) disp = comps.display # day with freeze_time("2019-01-01 22:59:59"): energy_saver = ESaver(comps, settings) time.sleep(energy_saver.INIT_WAIT_TIME) assert not energy_saver._update_thread is None assert not energy_saver.night_mode_active assert disp.get_brightness() == 70 time.sleep(energy_saver.UPDATE_TIME + 1) assert disp.get_brightness() == STANDBY_BRIGHTNESS assert not energy_saver.night_mode_active with freeze_time("2019-01-01 23:00:01"): time.sleep(2 * energy_saver.UPDATE_TIME + 1) assert energy_saver.night_mode_active assert disp.get_brightness() == NIGHT_MODE_BRIGHTNESS with freeze_time("2019-01-02 04:59:01"): time.sleep(energy_saver.UPDATE_TIME + 1) assert energy_saver.night_mode_active assert disp.get_brightness() == NIGHT_MODE_BRIGHTNESS energy_saver.stop()
def testDefaultComponentCreation(base_fixture, target_mockup_fixture): # test, that all components can be instantiated settings = Settings(base_fixture.testdata_path / "integration") settings.set(MOTION_SENSOR_ENABLED, True) settings.set(DHT_22_PIN, 15) settings.set(BME_280_ENABLED, True) comps = ComponentRegistry(settings) disp = comps.display assert disp temp = comps.temp_sensor assert temp mot = comps.motion_detection_sensor assert mot tts = comps.tts assert tts es = comps.energy_saver assert es weather_info = comps.weather_info assert weather_info au = comps.auto_updater assert au hum = comps.humidity_sensor assert hum ps = comps.pressure_sensor assert ps co2 = comps.co2_sensor assert co2 tvoc = comps.tvoc_sensor assert tvoc rt = comps.remote_temp_sensor assert rt ev = comps.event_handler assert ev
def testWakeUpFromNightMode(base_fixture, target_mockup_fixture): settings = Settings(base_fixture.testdata_path / "integration") settings.set(MOTION_SENSOR_ENABLED, True) settings.set(NIGHT_MODE_BEGIN, 22) settings.set(NIGHT_MODE_END, 5) settings.set(BRIGHTNESS, 70) settings.set(NIGHT_STANDBY_TIMEOUT, 10) energy_saver = None comps = ComponentRegistry(settings) disp = comps.display with freeze_time("2019-01-01 22:59:59"): energy_saver = ESaver(comps, settings) time.sleep(energy_saver.INIT_WAIT_TIME) time.sleep(energy_saver.UPDATE_TIME + 1) assert NIGHT_MODE_BRIGHTNESS == disp.get_brightness() assert energy_saver.night_mode_active == True # set sensor high comps.motion_detection_sensor._motion_detected = 1 time.sleep(energy_saver.UPDATE_TIME + 1) assert disp.get_brightness() == 70 - NIGHTMODE_WAKEUP_DELTA_BRIGHTNESS # set sensor low comps.motion_detection_sensor._motion_detected = 0 time.sleep(10 + 1) assert disp.get_brightness() == NIGHT_MODE_BRIGHTNESS energy_saver.stop()
def testComponentRegistryDefaultSensors(base_fixture): # disable every hw sensor settings = Settings(base_fixture.testdata_path / "integration") settings.set(DHT_22_PIN, 0) # settings.set() = 0 cr = ComponentRegistry(settings)
def testComponentRestartWatchdog(base_fixture, target_mockup_fixture): # test, that a sensor revives after stopping it settings = Settings(base_fixture.testdata_path / "integration") settings.set(DHT_22_PIN, 15) comps = ComponentRegistry(settings) temp = comps.temp_sensor hum = comps.humidity_sensor assert temp comps.stop_component_instance(temp) time.sleep(1) assert not hasattr(comps._components, "DHT22") assert not hasattr(comps._components, "TempSensor") assert not hasattr(comps._components, "HumiditySensor") assert comps.temp_sensor assert comps.humidity_sensor assert comps._components["DHT22"]
def testOnSoundDisabled(base_fixture, capsys): settings = Settings(base_fixture.testdata_path / "integration") settings.set(SOUND_ENABLED, "en") comps = ComponentRegistry(settings) tts = TextToSpeach(comps, settings) tts.say("Text1", "en") tts.wait_for_tts() # we can implicitly check, if the Thread has been started by us assert "TTS" in tts._tts_thread.getName() # test that no warning was thrown captured = capsys.readouterr() assert "WARNING" not in captured.out assert "Sound: Cannot play sound" not in captured.out
def testComponent(base_fixture): # test default constructor TestComponent = Component() # check default settings assert not TestComponent.is_disabled assert not TestComponent.reload_forbidden assert isinstance(TestComponent._logger, logging.Logger) assert isinstance(TestComponent._runtime_system, RuntimeSystem) assert TestComponent._settings is None assert TestComponent._comps is None settings = Settings(base_fixture.testdata_path / "integration") TestComponent = Component(settings=settings) assert TestComponent._settings == settings components = ComponentRegistry(settings) TestComponent = Component(components) assert TestComponent._comps == components
def testCCS811(base_fixture, target_mockup_fixture): from adafruit_ccs811 import TVOC, CO2 settings = Settings(base_fixture.testdata_path / "integration") comps = ComponentRegistry(settings) sensor = sensors.CCS811(comps, settings) measure_points = 2 sensor._co2_impl._values_capacity = measure_points sensor._tvoc_impl._values_capacity = measure_points time.sleep(1) assert sensor.is_alive assert sensor.is_ready # wait until all measurement points are filled up, so that mean value equals the constant value time.sleep(sensor.UPDATE_TIME * (measure_points + 1)) assert sensor.get_tvoc() == TVOC assert sensor.get_co2() == CO2
def testNoStandbyIfSensorIsDisabled(base_fixture): settings = Settings(base_fixture.testdata_path / "integration") settings.set(MOTION_SENSOR_ENABLED, False) settings.set(NIGHT_MODE_BEGIN, 23) settings.set(NIGHT_MODE_END, 5) settings.set(BRIGHTNESS, 70) energy_saver = None comps = ComponentRegistry(settings) disp = comps.display # night mode - no wakeup with freeze_time("2019-01-01 12:00:00"): # energy_saver needs to be initalized in freeze time, otherwise testing time will have an impact energy_saver = ESaver(comps, settings) time.sleep(energy_saver.INIT_WAIT_TIME) time.sleep(energy_saver.UPDATE_TIME + 1) assert disp.get_brightness() == settings.get(BRIGHTNESS) energy_saver.stop()
def testDHT22(base_fixture, target_mockup_fixture): from adafruit_dht import TEMP, HUM settings = Settings(base_fixture.testdata_path / "integration") comps = ComponentRegistry(settings) sensors.DHT22.UPDATE_TIME = 1 sensor = sensors.DHT22(pin=10, components=comps, settings=settings) measure_points = 2 sensor._temp_impl._values_capacity = measure_points sensor._hum_impl._values_capacity = measure_points time.sleep(1) assert sensor.is_alive assert sensor.is_ready # wait until all measurement points are filled up, so that mean value equals the constant value time.sleep(sensor.UPDATE_TIME * (measure_points + 1)) assert sensor.get_humidity() == HUM assert sensor.get_temperature() == TEMP
def testBMP280(base_fixture, target_mockup_fixture): from adafruit_bme280 import TEMP, PRESSURE settings = Settings(base_fixture.testdata_path / "integration") sensors.BME280.UPDATE_TIME = 1 comps = ComponentRegistry(settings) sensor = sensors.BME280(comps, settings) measure_points = 2 sensor._temp_impl._values_capacity = measure_points sensor._pres_impl._values_capacity = measure_points time.sleep(1) assert sensor.is_alive assert sensor.is_ready # wait until all measurement points are filled up, so that mean value equals the constant value time.sleep(sensor.UPDATE_TIME * (measure_points + 1)) assert sensor.get_pressure() == PRESSURE assert sensor.get_temperature() == TEMP
def testComponentRegistry(base_fixture): settings = Settings(base_fixture.testdata_path / "integration") cr = ComponentRegistry(settings) assert not cr._unload_in_progress comp = cr._create_component_instance(Component) cyc_comp = cr._create_component_instance(CyclicComponent) assert isinstance(comp, Component) assert isinstance(cyc_comp, CyclicComponent) assert "Component" in cr.get_names() assert "CyclicComponent" in cr.get_names() assert cr._components["Component"] == comp assert cr._components["CyclicComponent"] == cyc_comp
class ComponentController(): """ Loader, unloader and watchdog for components. """ UPDATE_TIME = 5 def __init__(self, settings: Settings): self._components = ComponentRegistry(settings) # thread for watchdog self._watch_thread: Optional[ threading. Thread] = None # re-usable thread, assignment is in init_all self._stop_event = threading.Event() # own stop event for watchdog # thread for waiting for comps unload self._unload_thread: Optional[ threading. Thread] = None # re-usable thread, assignment is in unload_all @property def all_ready(self) -> bool: """ Signals, that all modules have been started loading """ all_ready = False for comp_name in self._components.get_names(): component = self._components.get(comp_name) if component: all_ready |= component.is_ready return all_ready @property def all_unloaded(self) -> bool: """ All managed modules are unloaded. """ if not self._unload_thread: return True return not self._unload_thread.is_alive() @property def components(self) -> ComponentRegistry: """ Returns held components for higher level functions """ return self._components def init_all(self): """ Start every managed module, by starting the watch thread. """ if self._unload_thread and self._unload_thread.is_alive(): self._unload_thread.join() self._stop_event.clear() Logger().info("Start initializing all components") self._watch_thread = threading.Thread(name="Watchdog", target=self._watchdog_loop, daemon=True) self._watch_thread.start() def unload_all(self, reload_intended=False, updating=False): """ Start unloading modules. modules_unloaded signals finish. """ self._components.set_unload_in_progress() Logger().info("Start unloading all components") self._unload_thread = threading.Thread( name="UnloadModules", target=self._unload_all_components, args=[reload_intended, updating]) self._unload_thread.start() def stop(self): """ Stop this module, by sending a stop request. Actual stop is asyncron. """ if self._watch_thread and self._watch_thread.is_alive(): self._stop_event.set() def _watchdog_loop(self): time.sleep(2) # wait for execution self._components.show() ticker = threading.Event() while not ticker.wait(self.UPDATE_TIME): if self._stop_event.is_set(): self._stop_event.clear() return self._components.show() self._watch_components() def _watch_components(self): """ Checks existence of global variable of each module and starts it. """ # check and restart wifi if RuntimeSystem().is_target_system: RuntimeSystem().check_internet_connection() for comp_name in self._components.get_names(): component = self._components.get(comp_name) if not component: break if issubclass(type(component), CyclicComponent): if component.is_ready and not component.is_alive and not component.is_disabled: # call stop, so it will be initialized in the next cycle self._components.stop_component(comp_name) for sensor_name in self._components.get_sensors(): sensor = self._components.get_sensors()[sensor_name] if not sensor: break if isinstance(sensor, CyclicComponent ) and sensor.is_ready and not sensor.is_alive: self._components.get_sensors().pop(sensor_name) def _unload_all_components(self, reload_intended, updating): """ Stop own watcher and unload modules. :param reload_intended: singals, that objects, which forbid reload will be skipped """ # watch threads needs to stop - updater runs continously self.stop() if self._watch_thread: self._watch_thread.join() for comp_name in self._components.get_names(): if updating: # exclude updater if self._components.auto_updater == self._components.get( comp_name): continue self._components.stop_component(comp_name, reload_intended) Logger().info("ComponentRegistry: All components unloaded.") self._components.set_unload_finished()
def testRepoIsReachable(base_fixture): settings = Settings(base_fixture.testdata_path / "integration") comps = ComponentRegistry(settings) online_updater = OnlineUpdater(comps, enabled=True, use_beta_channel=True) online_updater._connect_to_repository() assert online_updater._repository # only check if object exists
def testCheckShouldUpdate(base_fixture): import waqd.components.updater as updater # import the module here, so we can access the loaded global var of WAQD version settings = Settings(base_fixture.testdata_path / "integration") comps = ComponentRegistry(settings) online_updater = OnlineUpdater(comps, enabled=True, use_beta_channel=True) # Main versions to Main versions updater.WAQD_VERSION = "1.1.0" # same version - no update assert not online_updater._check_should_update("v1.1.0") # use v to see if it is cut # lesser version - no update assert not online_updater._check_should_update("1.0.0") # higher version assert online_updater._check_should_update("1.2.0") # Main Version # Main version to Beta version # beta flag enabled # lesser version - no update assert not online_updater._check_should_update("1.0.0b19") # same version - but Beta -> must be older assert not online_updater._check_should_update("1.1.0b2") # higher version assert online_updater._check_should_update("1.2.0b0") # beta flag disabled - no update online_updater._use_beta_channel = False assert not online_updater._check_should_update("1.0.0b19") assert not online_updater._check_should_update("1.1.0b2") # Beta Version # Beta Version to Main Version updater.WAQD_VERSION = "1.1.0b1" assert not online_updater._check_should_update("1.0.0") assert online_updater._check_should_update("1.2.0") # Beta Version to Beta Version online_updater._use_beta_channel = True assert not online_updater._check_should_update("1.1.0b0") assert online_updater._check_should_update("1.1.0b2") online_updater._use_beta_channel = False assert not online_updater._check_should_update("1.1.0b2") # Beta Version to Alpha Version # negative test, not enabled assert not online_updater._check_should_update("1.1.0a2") # enable - higher version should work updater.config.DEBUG_LEVEL = 1 online_updater._use_beta_channel = True assert online_updater._check_should_update("1.1.0a2") # lower or equal should not assert not online_updater._check_should_update("1.1.0a1") assert not online_updater._check_should_update("1.1.0a0") # Alpha Version updater.WAQD_VERSION = "1.1.0a1" online_updater._use_beta_channel = True updater.config.DEBUG_LEVEL = 1 # to alpha assert online_updater._check_should_update("1.1.0a2") assert not online_updater._check_should_update("1.1.0a0") assert not online_updater._check_should_update("1.1.0a1") # to beta assert online_updater._check_should_update("1.1.0b2") assert online_updater._check_should_update("1.1.0b1") # TODO currently will always update to beta assert online_updater._check_should_update("1.1.0b0") # to main assert online_updater._check_should_update("1.1.0") assert online_updater._check_should_update("1.2.0") assert not online_updater._check_should_update("1.0.0") # disabled debug level - only update to beta or main updater.config.DEBUG_LEVEL = 0 # TODO: updates from alpha to alpha assert online_updater._check_should_update("1.1.0a2") assert online_updater._check_should_update("1.1.0b1") assert online_updater._check_should_update("1.1.0")