def setConfig(self, config: PluginConfig): super().setConfig(config) self._config = config for display in config.get("displays/list", default=[]): d = Display.from_dict(display) d.connected = False d.connected_via = "" self._display_list[hash(d)] = d
def __init__(self, config: PluginConfig, log:Logger): threading.Thread.__init__(self) self.__serial_port = config.get("serial", "/dev/ttyAMA0") self._log = log.getChild("Connection") self._device = DeviceInfo() self._device.model = None self._device_ready = False self._shutdown = False self._ved = None self._calls = {} self.name = "VE.Direct Serial"
class KaifaPluginConfig: def __init__(self, conf: conf.BasicConfig): self.c = PluginConfig(conf, PluginLoader.getConfigKey()) meters = self.c.get("meter", default=None) if not isinstance(meters, list): meters = [] self.c["meters"] = [] self.currIndex = len(meters) - 1 def addSmartMeter(self): meter = {} meter["port"] = ConsoleInputTools.get_input("Serielle Schnittstelle", require_val=True) meter["baudrate"] = ConsoleInputTools.get_number_input("Baudrate", map_no_input_to=2400) meter["parity"] = ConsoleInputTools.get_input("Parity", require_val=False, std_val="serial.PARITY_NONE") meter["stopbits"] = ConsoleInputTools.get_input("Stopbits", std_val="serial.STOPBITS_ONE") meter["bytesize"] = ConsoleInputTools.get_input("Bytesize", std_val="serial.EIGHTBITS") meter["key_hex_string"] = ConsoleInputTools.get_input("Entschlüsselungskey", require_val=True) meter["interval"] = ConsoleInputTools.get_number_input("Interval (Nicht mehr als 5)", map_no_input_to=1) meter["supplier"] = ConsoleInputTools.get_input("Netz Betreiber", std_val="EVN") self.c["meters"].append(meter) self.currIndex += 1 def remSmartMeter(self): print("Auflistung aller Seriellen Ports:") print("0 um zu beenden.") for index in len(self.c["meter"]): port = self.c[f"meter/{index}"] print(f"{index+1}. {port}") toDelete = ConsoleInputTools.get_number_input("Nummer wählen", map_no_input_to=0) if toDelete == 0: return toDelete -= 0 del self.c[f"meter/{toDelete}"] def run(self): while True: print("Mögliche Aktionen: \n 1. Smart Meter hinzufügen | 2. Smart Meter löschen\n 0. Beenden") action = ConsoleInputTools.get_number_input("Aktion", "0") match action: case 0: return case 1: self.addSmartMeter() case 2: self.remSmartMeter() case _: print("Ungültige Auswahl")
class WMI_PnP: _all_services = [] _pnp_cache: dict[str,dict] = {} _pnp_cache_rdy = False _shutdown = False _registered_devices: dict[str,BinarySensor.BinarySensor] = {} _pman: PluginManager = None def deviceRemovedThread(self): raw_wql = "SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA \'Win32_PnPEntity\'" # for removed devices c = wmi.WMI () watcher = c.watch_for(raw_wql=raw_wql) while not self._pnp_cache_rdy: sleep(1) self._log.debug("PnP cache ready. Beginne aktives warten auf events") while not self._shutdown: try: pnp = watcher(2500) id, entry = self._getPopulatedPnP(pnp) if id is None: continue if id in self._pnp_cache.keys(): self._log.debug("Gerät {} getrennt. sende update...".format(id)) self.addDevice(id, entry, False) self.sendUpdate(True) except wmi.x_wmi_timed_out: pass def deviceAddedThread(self): raw_wql = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA \'Win32_PnPEntity\'" # for added devices c = wmi.WMI () watcher = c.watch_for(raw_wql=raw_wql) while not self._pnp_cache_rdy: sleep(1) self._log.debug("PnP cache ready. Beginne aktives warten auf events") while not self._shutdown: try: pnp = watcher(2500) self._log.debug("New Device plugged in: {}".format(pnp)) id, entry = self._getPopulatedPnP(pnp) if id is None: continue self._log.debug("Device is allowed") self.addDevice(id, entry, True) self.sendUpdate(True) except wmi.x_wmi_timed_out: pass def _getPopulatedPnP(self, pnp) -> Tuple[str, dict]: pnp_entry = {} service = pnp.wmi_property("Service").value if service not in self._all_services: self._all_services.append(service) if service not in self._allowed_services: return None, None for props in pnp.properties.keys(): property = pnp.wmi_property(props) pnp_entry[property.name] = property.value return pnp.id, pnp_entry def __init__(self, config: PluginConfig, log: Logger) -> None: self._config = PluginConfig(config, "PnP") self._allowed_services = self._config.get("allowed", ["monitor"]) self._log = log.getChild("WMI_PnP") self._log.debug("Erstelle Watcher für PnP änderung...") self._pnp_added_thread = Thread(name="wmiPnpAdded", target=self.deviceAddedThread) self._pnp_added_thread.start() self._pnp_removed_thread = Thread(name="wmiPnpRemoved", target=self.deviceRemovedThread) self._pnp_removed_thread.start() self._log.info("Holen der Geräte informationen...") # Get All PnP Devices obj = wmi.WMI().Win32_PnPEntity() #Filter for Monitors for x in obj: self._log.debug("Verarbeite {}...".format(x.id)) id, entry = self._getPopulatedPnP(x) if id is not None: self.addDevice(id, entry, True) self._log.debug("Erlaubtes Gerät hinzugefügt.") self._pnp_cache_rdy = True self._log.info("Geräte ausgelesen") def register(self, wasConnected: bool, pman: PluginManager): self._pman = pman self.sendUpdate(not wasConnected) def addDevice(self, id: str, dev: dict, isConnected=False): if not isConnected and id in self._pnp_cache.keys(): self._pnp_cache.pop(id) elif isConnected and id not in self._pnp_cache.keys(): self._pnp_cache[id] = dev if self._pman is not None: if id not in self._registered_devices.keys(): if id not in self._config.get("devices", {}).keys(): self._config["devices"][id] = { "Name": dev["Name"], "Service": dev["Service"], "PnPID": id, "DeviceID": dev["DeviceID"] } uid = "bsens.win32.wmi.PnP.{}.{}".format(id, dev["Service"]) sensor = BinarySensor.BinarySensor( self._log, pman=self._pman, name=dev["Name"], binary_sensor_type=BinarySensorDeviceClasses.PLUG, value_template="{{value_json.pnp_present}}", json_attributes=True, unique_id=uid, subnode_id=dev["DeviceID"] ) self._registered_devices[id] = sensor sensor.register() self.sendDeviceState(id) else: sensor = self._registered_devices[id] sensor.register() self.sendDeviceState(id) def sendDeviceState(self, PnPDeviceID: str, forceOff=False): dev = self._registered_devices[PnPDeviceID] dev_pluggedin = 1 if PnPDeviceID in self._pnp_cache.keys() and not forceOff else 0 self._log.debug("Gerät {} is da? {}. [{}] Update wird gesendet!".format(PnPDeviceID, dev_pluggedin, self._pnp_cache.keys())) js = { } js.update(self._config.get("devices", {}).get(PnPDeviceID,{})) try: js.update(self._pnp_cache.get(PnPDeviceID, None).copy()) except: pass js["pnp_present"] = dev_pluggedin dev.turn(js) def _rebuild_devices(self): devices = self._config.get("devices", {}) for PNPDeviceID in devices.keys(): self.addDevice(PNPDeviceID, devices[PNPDeviceID], isConnected=PNPDeviceID in self._pnp_cache.keys()) for PNPDeviceID in self._pnp_cache.keys(): self.addDevice(PNPDeviceID, self._pnp_cache[PNPDeviceID], isConnected=True) def sendUpdate(self, cache_changed=False): if cache_changed: self._rebuild_devices() for PnPDeviceID in self._registered_devices: self.sendDeviceState(PnPDeviceID) def shutdown_watchers(self): self._shutdown = True self._log.debug("Warte auf added Thread...") self._pnp_added_thread.join() self._log.debug("Warte auf removed Thread...") self._pnp_removed_thread.join() self._log.debug("WMI PnP Überwachung beendet") for PnPDeviceID in self._registered_devices: self.sendDeviceState(PnPDeviceID, True)
class WindowEventProcessor: _shutdown = False _pman: Union[PluginManager,None] = None _hwnd: Union[int,None] = 0 _window_message_receivers: list[WindowEventReciever] = [] def __init__(self, config: PluginConfig, log: Logger) -> None: self._config = PluginConfig(config, "pwr") self._log = log.getChild("WindowMessages") if self._config.get("pwr", True): from Mods.win32submods.pwr.powerevents import Powerevents pe = Powerevents(self) self._window_message_receivers.append(pe) if self._config.get("dev", True): from Mods.win32submods.pwr.pwr_devices import DeviceManagementMessagesProcessor pe = DeviceManagementMessagesProcessor(self) self._window_message_receivers.append(pe) def wndproc(hwnd, msg, wparam, lparam): for rec in self._window_message_receivers: try: val = rec.on_window_event(hwnd, msg, wparam, lparam) if val is not None: return val except: self._log.exception("wndproc") def window_pump(): self._log.debug("Win32 API Window erstellen...") hinst = win32api.GetModuleHandle(None) wndclass = win32gui.WNDCLASS() wndclass.hInstance = hinst wndclass.lpszClassName = "mqttScriptPowereventWindowClass" CMPFUNC = CFUNCTYPE(c_bool, c_int, c_uint, c_uint, c_void_p) wndproc_pointer = CMPFUNC(wndproc) wndclass.lpfnWndProc = {win32con.WM_POWERBROADCAST : wndproc_pointer} try: myWindowClass = win32gui.RegisterClass(wndclass) self._hwnd = win32gui.CreateWindowEx(win32con.WS_EX_LEFT, myWindowClass, "mqttScriptPowereventWindow", 0, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, win32con.HWND_MESSAGE, 0, hinst, None) except Exception as e: self._log.exception("Window konnte nicht erstellt werden!") if self._hwnd is None: self._log.error("hwnd is none!") return else: self._log.debug("Windows Handle ({}) erstellt".format(self._hwnd)) self._log.debug("Begin pumping...") try: while not self._shutdown: win32gui.PumpWaitingMessages() time.sleep(1) except: self._log.exception("Pumping of messages failed") self._window_pump_thread = threading.Thread(name="window_pump", target=window_pump) self._window_pump_thread.start() def killPluginManager(self): if self._pman is None: self._log.error("Shutdown on windows shutdown failed!") return import threading t = threading.Thread(target=self._pman.shutdown, name="WindowsAsyncDestroy", daemon=True) t.start() def register(self, wasConnected: bool, pman: PluginManager): self._pman = pman for rec in self._window_message_receivers: rec.register(wasConnected=wasConnected) def shutdown(self): try: for rec in self._window_message_receivers: rec.shutdown() except: self._log.exception("Shutdown of WindowEvent extension failed!") self._log.debug("WindowEvent wait4shutdown...") self._shutdown = True self._window_pump_thread.join() def sendUpdate(self, force=True): try: for rec in self._window_message_receivers: rec.sendUpdate(force=force) except: self._log.exception("sendUpdate()")
class PiMotionMain(threading.Thread): _motionStream = etc.NullOutput() _webStream = etc.NullOutput() _circularStream = None _inMotion = None _pluginManager = None _rtsp_server = None _http_server = None _rtsp_split = None _jsonOutput = None _motion_topic = None _debug_topic = None _do_record_topic = None _lastState = {"motion": 0, "x": 0, "y": 0, "val": 0, "c": 0} _analyzer = None _postRecordTimer = None _pilQueue = None _pilThread = None _http_out = None _sendDebug = False _splitStream = None _record_factory = None _snapper = None _annotation_updater = None _err_topics = None _brightness_topic = None __last_brightness = nan bitrate = 17000000 _area = 25 # number of connected MV blocks (each 16x16 pixels) to count as a moving object _frames = 4 # number of frames which must contain movement to trigger def __init__(self, client: mclient.Client, opts: BasicConfig, logger: logging.Logger, device_id: str): threading.Thread.__init__(self) self.__client = client self.__logger = logger.getChild("PiMotion") self._config = PluginConfig(opts, "PiMotion") self._device_id = device_id self.__logger.debug("PiMotion.__init__()") self.name = "PiCamera" self.__logger.debug("PiMotion.register()") self._doExit = False self._camera = None path = self._config.get("record/path", "~/Videos") if not path.endswith("/"): path += "/" path = "{}/aufnahmen/".format(path) pathlib.Path(path).mkdir(parents=True, exist_ok=True) path = self._config.get("record/path", "~/Videos") if not path.endswith("/"): path += "/" path = "{}/magnitude/".format(path) pathlib.Path(path).mkdir(parents=True, exist_ok=True) self._image_font = ImageFont.truetype(font=self._config.get( "font", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"), size=9, encoding="unic") self._pilQueue = queue.Queue(5) self._splitStream = None self._err_topics = None self.was_errored = True self.set_do_record(self._config.get("record/enabled", True)) self.start() self._pilThread = threading.Thread(target=self.pil_magnitude_save, name="MagSave") self._pilThread.start() def set_do_record(self, recording: bool): self.__logger.info( "Config Wert aufnehmen wird auf {} gesetzt".format(recording)) self._config["record/enabled"] = recording def register(self, wasConnected=False): # Setup MQTT motion binary_sensor sensorName = self._config["motion/sensorName"] uid_motion = "binary_sensor.piMotion-{}-{}".format( self._device_id, sensorName) self._motion_topic = self._config._main.get_autodiscovery_topic( autodisc.Component.BINARY_SENROR, sensorName, autodisc.BinarySensorDeviceClasses.MOTION) motion_payload = self._motion_topic.get_config_payload( sensorName, "", unique_id=uid_motion, value_template="{{ value_json.motion }}", json_attributes=True) if self._motion_topic.config is not None: self.__client.publish(self._motion_topic.config, payload=motion_payload, qos=0, retain=True) errName = "{} Kamera Fehler".format(sensorName) self._err_topics = self._config.get_autodiscovery_topic( autodisc.Component.BINARY_SENROR, errName, autodisc.BinarySensorDeviceClasses.PROBLEM) self._err_topics.register( self.__client, errName, "", value_template="{{value_json.err}}", json_attributes=True, unique_id="cam.main.error.{}".format( self._config._main.get_client_config().id)) self.__client.publish(self._err_topics.state, json.dumps({ "err": 1, "Grund": "Starting..." })) self._brightness_topic = self._config.get_autodiscovery_topic( autodisc.Component.SENSOR, "Bildhelligkeit", autodisc.SensorDeviceClasses.ILLUMINANCE) self._brightness_topic.register( self.__client, "Bildhelligkeit", "pxLux", value_template="{{value_json.brightness}}", json_attributes=True, unique_id="cam.main.bright.{}".format( self._config._main.get_client_config().id)) self.was_errored = True def set_pluginManager(self, p: pm.PluginManager): self._pluginManager = p def stop(self): self._doExit = True if self._record_factory: self._record_factory.destroy() if self._pilQueue is not None and self._pilThread is not None: self.__logger.info("Stoppe PIL queue...") try: self._pilQueue.put((None, None), block=False) except queue.Full: pass if self._rtsp_server is not None: self._rtsp_server.stopServer() if self._http_server is not None: self._http_server.stop() self.__client.publish(self._motion_topic.ava_topic, "offline", retain=True) if self._analyzer is not None: self._analyzer.stop_queue() if self._pilQueue is not None and self._pilThread is not None: self.__logger.info("Warte auf PIL Thread...") self._pilThread.join(10) self._pilQueue = None if self._http_out is not None: self._http_out.flush() def setupAnalyzer(self, camera: cam.PiCamera): anal = analyzers.Analyzer(camera, logger=self.__logger.getChild("Analyzer"), config=self._config, fps=self._config["camera/fps"], postSecs=self._config.get( "motion/recordPost", 1)) # SET SETTINGS anal.frameToTriggerMotion = self._config.get("motion/motion_frames", 4) anal.framesToNoMotion = self._config.get("motion/still_frames", 4) anal.blockMinNoise = self._config.get("motion/blockMinNoise", 0) anal.countMinNoise = self._config.get("motion/frameMinNoise", 0) anal.countMaxNoise = self._config.get("motion/frameMaxNoise", 0) anal.lightDiffBlock = self._config.get("motion/lightDiffBlock", -1) anal.zeromap_py = self._config.get("zeroMap", { "enabled": False, "isBuilding": False, "dict": None }) anal.disableAnalyzing = not self._config.get("motion/doAnalyze", True) # SET CALLBACKS anal.motion_call = lambda motion, data, mes: self.motion( motion, data, mes) anal.motion_data_call = lambda data, changed: self.motion_data( data, changed) anal.pil_magnitude_save_call = lambda img, data: self.pil_magnitude_save_call( img, data) anal.cal_getMjpeg_Frame = self.getMjpegFrame self._analyzer = anal def setupRTSP(self): factory = PiCameraMediaFactory( fps=self._config["camera/fps"], CamName=self._config["motion/sensorName"], log=self.__logger.getChild("RTSP_Factory"), splitter=self. _splitStream #,wh=(self._config["camera/width"], self._config["camera/height"]) ) server = GstServer(factory=factory, logger=self.__logger) server.runServer() self._rtsp_server = server def setupRecordFactory(self) -> PreRecordBuffer: factory = PreRecordBuffer( secs_pre=self._config["motion/recordPre"], fps=self._config["camera/fps"], camName=self._config["motion/sensorName"], logger=self.__logger, splitter=self._splitStream, path=pathlib.Path(self._config["record/path"]), wh=(self._config["camera/width"], self._config["camera/height"])) factory.start() self._record_factory = factory return factory def setupHttpServer(self): self.__logger.info("Aktiviere HTTP...") http_out = CameraSplitter(self._camera, self.__logger, 1, True) self._http_out = http_out self._jsonOutput = httpc.StreamingJsonOutput() address = (self._config.get("http/addr", "0.0.0.0"), self._config.get("http/port", 8083)) streamingHandle = httpc.makeStreamingHandler(http_out, self._jsonOutput) streamingHandle.logger = self.__logger.getChild("HTTPHandler") streamingHandle.meassure_call = lambda s, i: self.meassure_call(i) streamingHandle.fill_setting_html = lambda s, html: self.fill_settings_html( html) streamingHandle.update_settings_call = lambda s, a, b, c, d, e, f: self.update_settings_call( a, b, c, d, e, f) streamingHandle.jpegUpload_call = lambda s, d: self.parseTrainingPictures( d) streamingHandle.set_anal_onhold = lambda s, x: self.set_anal_onhold(x) streamingHandle.save_snapshot = lambda s: self.takeSnapshot() server = httpc.StreamingServer(address, streamingHandle) server.logger = self.__logger.getChild("HTTP_srv") self._http_server = server def run(self): import time time.sleep(5) self.__logger.debug("PiMotion.run()") with cam.PiCamera(clock_mode='raw', framerate=self._config.get("camera/fps", 23)) as camera: self._camera = camera self._splitStream = CameraSplitter(camera=camera, log=self.__logger) # Init Kamera camera.resolution = (self._config["camera/width"], self._config["camera/height"]) camera.video_denoise = self._config.get("camera/denoise", True) self.__logger.debug("Kamera erstellt") camera.annotate_background = cam.Color('black') camera.annotate_text = dt.datetime.now().strftime( 'Gestartet am %d.%m.%Y um %H:%M:%S') self.setupAnalyzer(camera) with self._analyzer: self.__logger.debug("Analyzer erstellt") if self._config.get("rtsp/enabled", True): self.setupRTSP() self.setupRecordFactory() self._splitStream.splitter_port = 1 camera.start_recording( self._splitStream, format='h264', profile='high', level='4.1', splitter_port=1, motion_output=self._analyzer, quality=25, sps_timing=True, intra_period=int(self._config.get("camera/fps", 23) * 0.5), sei=True, inline_headers=True, bitrate=self.bitrate) if self._config.get("http/enabled", False): self.setupHttpServer() camera.start_recording(self._http_out, format='mjpeg', splitter_port=2) t = threading.Thread(name="http_server", target=self._http_server.run) self.__logger.info("Starte HTTP...") t.start() # Und jetzt einfach warten def first_run(): time.sleep(2) self.__logger.info( "Stream wird sich normalisiert haben. Queue wird angeschlossen..." ) self._analyzer.run_queue() self.update_anotation() threading.Thread(target=first_run, name="Analyzer bootstrap", daemon=True).start() exception_raised = False sleep_seconds = 2 while not self._doExit: try: camera.wait_recording(sleep_seconds, splitter_port=1) if self._analyzer.disableAnalyzing: pps = self._splitStream.written / sleep_seconds self._splitStream.written = 0 else: pps = self._analyzer.processed / sleep_seconds self._analyzer.processed = 0 fps = self._config.get("camera/fps", 23) if int(fps) != int(pps): self.__logger.warning( "Pro Sekunde verarbeitet: %d, sollte aber %d sein", pps, fps) if pps == 0: self.was_errored = True if self.__client is not None: self.__client.publish( self._err_topics.state, json.dumps({ "err": 1, "Grund": "FPS ist 0" })) import sys, traceback with open("/tmp/piMotion_last_traces", "w") as f: for thread_id, frame in sys._current_frames( ).items(): print('\n--- Stack for thread {t} ---'. format(t=thread_id), file=f) traceback.print_stack(frame, file=f) elif self.was_errored: if self.__client is not None: self.__client.publish( self._err_topics.state, json.dumps({ "err": 0, "Grund": "Fehler verschwunden" })) self.was_errored = False self.sendBrightness() except Exception as e: self.__logger.exception("Kamera Fehler") self._doExit = True exception_raised = True self._analyzer.stop_queue(e) if not exception_raised: self._analyzer.stop_queue() try: self._http_server.stop() except: pass try: self._rtsp_server.stopServer() except: pass try: camera.stop_recording(splitter_port=2) except: pass try: camera.stop_recording() except: pass try: self._http_out.shutdown() except: pass self._camera = None def update_anotation(self, aps=0): self._annotation_updater = None if self._camera is not None: txt_motion = dt.datetime.now().strftime( '%d.%m.%Y %H:%M:%S REC' ) if self._inMotion else dt.datetime.now().strftime( '%d.%m.%Y %H:%M:%S STILL') self._camera.annotate_text = txt_motion self._annotation_updater = threading.Timer( interval=1, function=self.update_anotation) self._annotation_updater.start() #self._camera.annotate_text = text1 + " {} {}APS {} {} {} {}".format( # txt_motion, aps, # self._lastState["x"], self._lastState["y"], self._lastState["val"], self._lastState["c"] #) def meassure_call(self, i): if i == 0: self.meassure_minimal_blocknoise() elif i == 1: self._analyzer.trainZeroMap() elif i == 2: self._analyzer.trainZeroMap(True) def meassure_minimal_blocknoise(self): self.__logger.info("Starte neue Kalibrierung...") self._analyzer.states["still_frames"] = 0 self._analyzer._calibration_running = True self._analyzer.framesToNoMotion *= 15 def stop_record(self): self._postRecordTimer = None self._record_factory.stop_recording() try: self.motion(False, self._lastState, False, True) except KeyError: self.__logger.exception("Sending no motion failed!") def do_record(self, record: bool, stopInsta=False): if record: if self._config.get("record/enabled", True): path = self._config.get("record/path", "~/Videos") if not path.endswith("/"): path += "/" path = "{}/aufnahmen/{}.h264".format( path, dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) if self._postRecordTimer is None: self._record_factory.record() self._postRecordTimer = ResettableTimer( interval=self._config.get("motion/recordPost", 1), function=self.stop_record, autorun=False) if self._config.get("motion/takeSnapshot", False): self._snapper = threading.Timer( interval=self._config.get( "motion/takeSnapshotSeconds", 2), function=self.takeSnapshot) self._snapper.name = "PiMotionSnapshot" self._snapper.start() elif self._postRecordTimer is not None: self._postRecordTimer.cancel() self.__logger.debug("Aufnahme timer wird zurückgesetzt") else: self.__logger.info("No Motion") if self._postRecordTimer is not None: if stopInsta: self._postRecordTimer._interval = 1 self._postRecordTimer.reset() self._postRecordTimer.reset() self.__logger.debug( "Aufnahme wird in {} Sekunden beendet.".format( self._config.get("motion/recordPost", 1))) else: self.__logger.info("Kein Timer vorhanden. Stoppe sofort") self.stop_record() def takeSnapshot(self): if self._snapper is not None: self._snapper.cancel() self._snapper = None path = self._config.get("record/path", "~/Videos") if not path.endswith("/"): path += "/" path = "{}/aufnahmen/{}.jpeg".format( path, dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) self._camera.capture(path, "jpeg", thumbnail=(64, 48, 35)) def motion(self, motion: bool, data: dict, wasMeassureing: bool, delayed=False): if wasMeassureing: self._config["motion/blockMinNoise"] = self._analyzer.blockMaxNoise self._config["motion/frameMinNoise"] = self._analyzer.countMinNoise self._config["zeroMap"] = self._analyzer.zeromap_py self._config.save() if not delayed: self.do_record(motion) if not motion and not delayed: #delay the stop motion self._lastState = data return last = self._inMotion self._inMotion = motion self.motion_data(data=data, changed=last is not motion) #self.set_do_record(motion) def motion_data(self, data: dict, changed=False): # x y val count if data["type"] == "MotionDedector": self._lastState = { "motion": data["motion"], "val": data["val"], "type": data["type"], "brightness": data["brightness"], "lightDiff": data["brightness_change"] } elif data["type"] == "hotblock": self._lastState = { "motion": 1 if self._inMotion else 0, "x": data["hotest"][0], "y": data["hotest"][1], "val": data["hotest"][2], "c": data["noise_count"], "dbg_on": self._sendDebug, "ext": data["extendet"], "brightness": data["brightness"], "lightDiff": data["brightness_change"], "type": "hotBlock" } elif data["type"] == "brightness": self._lastState["brightness"] = data["brightness"] self._lastState["lightDiff"] = data["brightness_change"] self.sendBrightness() self._jsonOutput.write(self._lastState) if self._analyzer is not None and not self._analyzer._calibration_running: self.sendStates(changed=changed) #self.update_anotation() def sendBrightness(self): if self.__last_brightness != self._lastState.get( "brightness", nan) and not isnan( self._lastState.get("brightness", nan)): if self.__client is None: return self.__last_brightness = self._lastState.get("brightness", nan) self.__client.publish( self._brightness_topic.state, json.dumps({ "brightness": self._lastState.get("brightness", nan), "diff": self._lastState.get("lightDiff", 0) })) def sendStates(self, changed=None): if self.__client is None: return if changed is None: self.__client.publish(self._motion_topic.state, json.dumps(self._lastState)) self.__client.publish(self._debug_topic.state, json.dumps(self._lastState)) self.sendBrightness() elif changed: self.__client.publish(self._motion_topic.state, json.dumps(self._lastState)) self.sendBrightness() elif self._sendDebug: self.__client.publish(self._debug_topic.state, json.dumps(self._lastState)) def pil_magnitude_save_call(self, d, data: dict): if self._pilQueue is not None: try: self._pilQueue.put_nowait((d, data)) except queue.Full: pass def getMjpegFrame(self, block=True, timeout=-1): try: q = queue.Queue(1) def push_queue(frame, extendet, eof=False): try: q.put_nowait(frame) except queue.Full: pass aid = self._http_out.add(push_queue) frame = None try: frame = q.get(block=block, timeout=timeout) except queue.Empty: pass self._http_out.remove(aid) return frame except AttributeError: return None def pil_magnitude_save(self): while True: try: a, data = self._pilQueue.get(timeout=5) except queue.Empty: if self._doExit: self.__logger.warning( "PIL Magnitude Save Thread wird beendet") return continue if self._doExit or a is None and data is None: self.__logger.warning("PIL Magnitude Save Thread wird beendet") return frame = self.getMjpegFrame(block=True, timeout=2) background = None try: if frame is not None: background = Image.open(io.BytesIO(frame)) else: self.__logger.warning("Snapshot is None") except IOError: self.__logger.warning("Snapshot IOError") d = np.sqrt( np.square(a['x'].astype(np.float)) + np.square(a['y'].astype(np.float))).clip(0, 255).astype(np.uint8) Bimg = Image.fromarray(d) img = Bimg.convert(mode="RGB", dither=Image.FLOYDSTEINBERG) #self.__logger.debug(data) if data is not None: ig = ImageDraw.Draw(img) ig.point( [(data["object"][4]["col"], data["object"][4]["row"])], fill=(255, 0, 0, 200)) path = pathlib.Path(self._config.get("record/path", "~/Videos")) path = path.joinpath( "magnitude", "{}.jpeg".format( dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) path = path.expanduser().absolute() self.__logger.debug('Writing %s' % path) if background is not None: self.__logger.debug("Habe Snapshot. Vermische Bilder...") img = img.resize((self._config["camera/width"], self._config["camera/height"])) foreground = img.convert("RGBA") background = background.convert("RGBA") img = Image.blend(background, foreground, 0.5) exif_bytes = None if data is not None: draw = ImageDraw.Draw(img) draw.text( (0, 0), "X: {} Y: {} VAL: {} C: {}".format(data["hotest"][0], data["hotest"][1], data["hotest"][2], data["noise_count"]), fill=(255, 255, 0, 155), font=self._image_font) draw.text((0, 20), "R: {} C: {}".format(data["object"][4]["row"], data["object"][4]["col"]), fill=(255, 255, 0, 155), font=self._image_font) draw.text((0, 40), "B: {} Zdata: {}".format(data.get("brightness", -1), data.get("zmdata", "KEINE")), fill=(255, 255, 0, 155), font=self._image_font) draw.text((0, 60), "BDiff: {}".format(data["lightDiff"]), fill=(255, 255, 0, 155), font=self._image_font) draw.text((0, 80), dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), fill=(255, 255, 0, 155), font=self._image_font) with path.open(mode="wb") as file: if data is not None: cimg = img.convert('RGB') bio = io.BytesIO() cimg.save(bio, "jpeg") encoded = data #### OK Daten sind zu groß für exif villeicht anhängen? extendet_data = {} try: extendet_data = data.get("exif_zeromap", None) if extendet_data is not None: del data["exif_zeromap"] data["extendet_data_appendet"] = True encoded = json.dumps(data) user_comment = piexif.helper.UserComment.dump( encoded, encoding="unicode") bio.seek(0) exif_dict = piexif.load(bio.read()) exif_dict["Exif"][ piexif.ExifIFD.UserComment] = user_comment exif_bytes = piexif.dump(exif_dict) except: self.__logger.exception("exif_json failed") if exif_bytes is not None: bio = io.BytesIO() cimg.save(bio, "jpeg", exif=exif_bytes) if extendet_data is not None and len( extendet_data.keys()) > 0: bio.write(b"=======EXTENDET_DATA=======") js_Data = json.dumps(extendet_data) bio.write(js_Data.encode("utf-8")) bio.flush() bio.seek(0) shutil.copyfileobj(bio, file) file.flush() file.close() def getExtenetData(self, data: io.BytesIO): data.seek(0) buf = data.read() buf = buf.split(b"=======EXTENDET_DATA=======") if len(buf) > 1: js = json.loads(buf[1]) return js return {} def parseTrainingPictures(self, data: io.BytesIO): if data is None: self._analyzer.trainZeroMap(data=False) return try: data.seek(0) exif_dict = piexif.load(data.read()) ucb = exif_dict["Exif"][piexif.ExifIFD.UserComment] ucr = piexif.helper.UserComment.load(ucb) ucj = json.loads(ucr) if ucj.get("extendet_data_appendet", False): ucj["exif_zeromap"] = self.getExtenetData(data) self._analyzer.trainZeroMap(data=ucj) except: self.__logger.exception("parseTrainingPictures()") def fill_settings_html(self, html: str): cv = self._camera.color_effects if cv is None: cv = (-1, -1) html = html.format( self._analyzer.countMaxNoise, self._analyzer.countMinNoise, self._analyzer.blockMinNoise, self._analyzer.frameToTriggerMotion, self._analyzer.framesToNoMotion, self._analyzer.lightDiffBlock, self._camera.shutter_speed, self._camera.exposure_speed, self._camera.exposure_mode, cv[0], cv[1], self._camera.iso) return html def update_settings_call(self, countMaxNoise, countMinNoise, blockMinNoise, frameToTriggerMotion, framesToNoMotion, lightDiff): self.__logger.debug("HTTP Einstellungen werden gespeichert...") self._analyzer.countMaxNoise = countMaxNoise self._analyzer.countMinNoise = countMinNoise self._analyzer.blockMinNoise = blockMinNoise self._analyzer.frameToTriggerMotion = frameToTriggerMotion self._analyzer.framesToNoMotion = framesToNoMotion self._analyzer.lightDiffBlock = lightDiff self._config["motion/motion_frames"] = frameToTriggerMotion self._config["motion/still_frames"] = framesToNoMotion self._config["motion/blockMinNoise"] = blockMinNoise self._config["motion/frameMinNoise"] = countMinNoise self._config["motion/frameMaxNoise"] = countMaxNoise self._config["motion/lightDiffBlock"] = lightDiff self._config.save() def set_anal_onhold(self, on_hold=None) -> bool: if on_hold is not None: self._analyzer._on_hold = on_hold return self._analyzer._on_hold
class win32Systray: _shutdown = False _icon_rdy = False _pman: PluginManager = None _menuItemsInit: list[pystray.MenuItem] = [] _menuItemsDynamic: list[pystray.MenuItem] = [] def image(self, width=64, height=64) -> tuple[Image.Image, Colors]: """Generates an icon image. :return: the tuple ``(image, colors)``, where ``image`` is a *PIL* image and ``colors`` is a tuple containing the colours as *PIL* colour names, suitable for printing; the stringification of the tuple is also suitable for printing """ colors = Colors(("blue", "white")) img = Image.new('RGB', (width, height), colors[0]) dc = ImageDraw.Draw(img) dc.rectangle((width // 2, 0, width, height // 2), fill=colors[1]) dc.rectangle((0, height // 2, width // 2, height), fill=colors[0]) return img, colors def icon_created(self, icon): self._icon_rdy = True self._sicon.visible = True self._log.info("Systray Icon wird jetzt angezeigt.") def killPluginManager(self): if self._pman is not None: self._pman.shutdown() self._sicon.notify( "Interner Fehler (PluginManager nicht gesetzt), kann nicht normal beendet werden. Versuche exit()" ) exit(1) def sicon_thread(self): self._log.debug("Icon image wird gezeichnet...") img, colors = self.image() self._log.debug("Erstelle initales Menü...") items: list[pystray.MenuItem] = [] items.append(pystray.MenuItem("Warte auf verbindung...", None)) items.append(pystray.Menu.SEPARATOR) items.append(pystray.MenuItem("Beenden", self.killPluginManager)) self._menuItemsInit = tuple(items) self._menu = pystray.Menu(*items) self._log.debug("Beginne mit dem mainloop von pysystray...") self._sicon = pystray.Icon("mqttScripts", icon=img, menu=self._menu) self._sicon.run(setup=self.icon_created) def __init__(self, config: PluginConfig, log: Logger) -> None: self._config = PluginConfig(config, "systray") self._log = log.getChild("STRAY") self.icon_thread = Thread(name="systray", daemon=False, target=self.sicon_thread) self.icon_thread.start() @staticmethod def getNewDeviceEntry(): from Tools import ConsoleInputTools config = {} name = ConsoleInputTools.get_input("Name für den Eintrag?") config["type"] = "switch" if ConsoleInputTools.get_bool_input( "Soll der eintrag als Schalter fungieren?", False) else "action" config["confirm"] = ConsoleInputTools.get_bool_input( "Auswählen des Eintrages bestätigen?", False) return name, config def generateDeviceIems(self): citems: dict = self._config.get("itemList", {}) mitems: list[pystray.MenuItem] = [] for name, entry in citems.items(): ctype = entry.get("type", "action") if ctype == "action": bsensor = BinarySensor.BinarySensor( self._log, self._pman, name, BinarySensorDeviceClasses.GENERIC_SENSOR) bs = SensorDeviceItem(name, bsensor, confirm=entry.get("confirm", False)) bsensor.register() mitems.append(bs) elif ctype == "switch": sw = SwitchDeviceItem(name, entry.get("wasOn", False), self._config, self._log, self._pman) sw._confirm = entry.get("confirm", False) mitems.append(sw) mitems.append(pystray.Menu.SEPARATOR) mitems.append(pystray.MenuItem("Beenden", self.killPluginManager)) self._menu = pystray.Menu(*mitems) self._sicon.menu = self._menu self._menuItemsDynamic = mitems def updateDeviceItems(self): for mis in self._menuItemsDynamic: if isinstance(mis, SwitchDeviceItem): sdi: SwitchDeviceItem = mis sdi.device_action() def register(self, wasConnected: bool, pman: PluginManager): self._pman = pman if self._icon_rdy: self._sicon.notify( "Verbindung mit MQTT Broker {}hergestellt".format( "wieder" if wasConnected else "")) self.generateDeviceIems() def sendUpdate(self, force=True): if force: self.updateDeviceItems() def disconnected(self): self._menu.items = self._menuItemsInit self._sicon.notify("Verbindung zum MQTT Server unterbrochen!") def shutdown(self): self._shutdown = True if self._icon_rdy: self._sicon.stop()
class PID_Plugin: def __init__(self, client: mclient.Client, opts: BasicConfig, logger: logging.Logger, device_id: str): self.__client = client self.__logger = logger.getChild("PID") self._config = PluginConfig(opts, "PID") self._device_id = device_id self._hvac_call = PID_Controller(self._config, client, logger) self._device = None def set_pluginManager(self, pm): self._pluginManager = pm self._device = HvacDevice(self.__logger.getChild("dev"), pm, self._hvac_call, self._config.get("name")) def register(self, wasConnected: bool): if not wasConnected: self._hvac_call.initialize(self._device) self._hvac_call.register() def sendStates(self): self.sendUpdate() def sendUpdate(self, fromHandler=False): pass def on_message(self, client, userdata, message: mclient.MQTTMessage): pass def stop(self): if self._hvac_call is not None: self._hvac_call.stop()