diff --git a/handler.py b/handler.py index a948fcd..ba88a3f 100644 --- a/handler.py +++ b/handler.py @@ -448,7 +448,8 @@ def auto_check_and_lock_door(): lockDoor() if mqttObject: try: - mqttObject.publish_status(f"{time.strftime('%Y-%m-%d %H:%M:%S')} Türe wurde automatisch verriegelt (Sperrzeit).") + # timestamp automatically prepended by publish_status + mqttObject.publish_status("Türe wurde automatisch verriegelt (Sperrzeit).") except Exception as e: logger.debug(f"MQTT-Publish fehlgeschlagen: {e}") @@ -457,7 +458,7 @@ def auto_check_and_lock_door(): unlockDoor() if mqttObject: try: - mqttObject.publish_status(f"{time.strftime('%Y-%m-%d %H:%M:%S')} Türe wurde automatisch entriegelt (Sperrzeit vorbei).") + mqttObject.publish_status("Türe wurde automatisch entriegelt (Sperrzeit vorbei).") except Exception as e: logger.debug(f"MQTT-Publish fehlgeschlagen: {e}") diff --git a/mqtt.py b/mqtt.py index e0418ed..c8dcc93 100644 --- a/mqtt.py +++ b/mqtt.py @@ -7,6 +7,7 @@ import logging import time +from datetime import datetime from config import Config as config logger = logging.getLogger(__name__) @@ -109,8 +110,11 @@ def publish_status(message): return False if _client: - _client.publish(config.MQTT_TOPIC_MESSAGE, message) - logger.info(f"MQTT gesendet: {message}") + # prepend current date/time to every status message automatically + ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + full_message = f"{ts} {message}" + _client.publish(config.MQTT_TOPIC_MESSAGE, full_message) + logger.info(f"MQTT gesendet: {full_message}") return True else: logger.warning("MQTT-Client nicht verbunden, Nachricht nicht gesendet.") diff --git a/paketbox.py b/paketbox.py index 35ea4cf..7e55b2a 100644 --- a/paketbox.py +++ b/paketbox.py @@ -1,5 +1,5 @@ # Paketbox control script -# Version 0.8.9 +# Version 0.8.10 # import time import sys import logging @@ -86,18 +86,22 @@ def pinChanged(pin, oldState, newState): handler.Paket_Tuer_Zusteller_geoeffnet() if mqttObject: mqttObject.publish_paket_zusteller_event("ON") + mqttObject.publish_status("Paketbox wurde geöffnet.") elif pin == 5: logger.info(f"Briefkasten Zusteller geöffnet.") if mqttObject: mqttObject.publish_briefkasten_event("ON") + mqttObject.publish_status("Briefkasten wurde geöffnet.") elif pin == 6: logger.info(f"Briefkasten Türe zum Leeren geöffnet.") if mqttObject: mqttObject.publish_briefkasten_entleeren_event("ON") + mqttObject.publish_status("Briefkasten wurde geleert.") elif pin == 7: logger.info(f"Paketbox Türe zum Leeren geöffnet.") if mqttObject: mqttObject.publish_paketbox_entleeren_event("ON") + mqttObject.publish_status("Paketbox wurde geleert.") handler.setLigthtPaketboxOn() elif pin == 9: logger.info(f"Tür Mültonne geöffnet.") @@ -105,6 +109,8 @@ def pinChanged(pin, oldState, newState): elif pin == 10: logger.info(f"Lichtschranke ist nicht mehr ausgelöst.") handler.reset_light_barrier() + if mqttObject: + mqttObject.publish_status("Lichtschranke zurückgesetzt.") elif oldState == 1 and newState == 0: # falling edge if pin == 0: @@ -138,16 +144,18 @@ def pinChanged(pin, oldState, newState): if mqttObject: mqttObject.publish_paketbox_entleeren_event("OFF") handler.setLigthtPaketboxOff() - # handler.ResetErrorState() - # handler.ResetDoors() elif pin == 8: - logger.info(f"Türöffner Taster 6 gedrückt.") + logger.info(f"Reset wird ausgelöst.") + handler.ResetErrorState() + handler.ResetDoors() elif pin == 9: logger.info(f"Tür Mültonne geschlossen.") handler.lichtMueltonneOff() elif pin == 10: logger.info(f"Lichtschranke hat ausgelöst.") handler.set_light_barrier_triggered(True) + if mqttObject: + mqttObject.publish_status("Lichtschranke ausgelöst.") else: logger.warning(f"pinChanged: oldState == newState keine Änderung erkannt.") @@ -168,7 +176,7 @@ def main(): global mqttObject mqttObject = mqtt mqttObject.start_mqtt() - mqttObject.publish_status(f"{time.strftime('%Y-%m-%d %H:%M:%S')} Paketbox bereit.") + mqttObject.publish_status("Paketbox bereit.") # Initialize door states based on current GPIO readings statusOld = initialize_door_states() statusNew = [0] * len(Config.INPUTS) @@ -195,14 +203,14 @@ def main(): statusNew[i] = GPIO.input(pin) if statusNew[i] != statusOld[i]: pinChanged(i, statusOld[i], statusNew[i]) - logger.info(f"GPIO {pin} changed: {statusOld[i]} -> {statusNew[i]}") + logger.debug(f"GPIO {pin} changed: {statusOld[i]} -> {statusNew[i]}") statusOld[i] = statusNew[i] # Monitor for error conditions if ( not sendMqttErrorState and pbox_state.is_any_error()): logger.warning(f"WARNUNG: System im Fehlerzustand! {pbox_state}") if mqttObject: - mqttObject.publish_status(f"{time.strftime('%Y-%m-%d %H:%M:%S')} FEHLER Paketbox: {pbox_state}") + mqttObject.publish_status(f"FEHLER Paketbox: {pbox_state}") sendMqttErrorState = True except KeyboardInterrupt: diff --git a/tests/test_paketbox.py b/tests/test_paketbox.py index 1c57466..0b757b6 100644 --- a/tests/test_paketbox.py +++ b/tests/test_paketbox.py @@ -390,6 +390,24 @@ def test_motor_failure_setOutputWithRuntime_fails(self, mock_timer, mock_setOutp # Timer should not be started for endlagen_pruefung mock_timer.assert_not_called() + def test_publish_status_adds_timestamp(self): + """Verify that mqtt.publish_status prefixes messages with a date/time string.""" + import mqtt + # ensure MQTT_AVAILABLE is True and client is mocked + mqtt.MQTT_AVAILABLE = True + mock_client = MagicMock() + mqtt._client = mock_client + + result = mqtt.publish_status("Testnachricht") + self.assertTrue(result, "publish_status should return True when client present") + # ensure publish was called exactly once + mock_client.publish.assert_called_once() + topic, sent = mock_client.publish.call_args[0] + self.assertEqual(topic, mqtt.config.MQTT_TOPIC_MESSAGE) + # message should start with a timestamp like 'YYYY-MM-DD HH:MM:SS ' + import re + self.assertRegex(sent, r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Testnachricht$") + @patch('handler.get_gpio') @patch('handler.setOutputWithRuntime') @patch('handler.threading.Timer')