import 'package:flutter/foundation.dart'; import 'package:mqtt_client/mqtt_client.dart'; import 'package:mqtt_client/mqtt_server_client.dart'; const String brokerHostname = 'sia.xengineering.eu'; const int brokerPort = 1883; const String topicPrefix = 'sia'; enum MachineState { init, // user does not want connection MQTT not connected disconnected, // user wants connection but MQTT not (yet) connected } enum MachineEvent { connect, // user wants to connect disconnect, // user does not want an connection anymore } class AppState with ChangeNotifier { static const Map> machine = > { MachineState.init: { MachineEvent.connect: MachineState.disconnected, }, MachineState.disconnected: { MachineEvent.disconnect: MachineState.init, }, }; MachineState state = MachineState.init; Map contacts = {}; late MqttServerClient _client; bool _brokerConnected = false; bool get brokerConnected => _brokerConnected; bool _serverConnected = false; bool get serverConnected => _serverConnected; AppState(); void process(MachineEvent event) { MachineState lastState = state; Map? transitions = machine[lastState]; if (transitions == null) { return; } MachineState? nextState = transitions[event]; if (nextState == null) { return; } if (nextState == lastState) { return; } if (lastState == MachineState.init) { _initMqtt(); } if (nextState == MachineState.init) { _deinitMqtt(); } state = nextState; notifyListeners(); } @override void dispose() { _client.disconnect(); super.dispose(); } void _initMqtt() { _client = MqttServerClient( brokerHostname, 'sia_app_${DateTime.now().millisecondsSinceEpoch}', ); _client.port = brokerPort; _client.keepAlivePeriod = 2; _client.autoReconnect = true; _client.disconnectOnNoResponsePeriod = 1; _client.logging(on: false); _client.onConnected = _onConnected; _client.onDisconnected = _onDisconnected; _client.onAutoReconnect = _onAutoReconnect; _client.onAutoReconnected = _onAutoReconnected; try { _client.connect(); } catch (e) { _client.disconnect(); return; } _client.updates?.listen(_onMessage); } void _deinitMqtt() { _client.disconnect(); } void _onConnected() { _client.subscribe('$topicPrefix/contact/+/state', MqttQos.exactlyOnce); _client.subscribe('$topicPrefix/server/health', MqttQos.exactlyOnce); _brokerConnected = true; notifyListeners(); } void _onDisconnected() { _brokerConnected = false; notifyListeners(); } void _onAutoReconnect() { _brokerConnected = false; notifyListeners(); } void _onAutoReconnected() { _brokerConnected = true; notifyListeners(); } void _onMessage(List> messages) { for (final MqttReceivedMessage message in messages) { final String topic = message.topic; final MqttPublishMessage payloadMessage = message.payload as MqttPublishMessage; final String payload = MqttPublishPayload.bytesToStringAsString(payloadMessage.payload.message); if (topic == '$topicPrefix/server/health') { if (payload == 'good') { _serverConnected = true; notifyListeners(); } if (payload == 'bad') { _serverConnected = false; notifyListeners(); } } // format /contacts/
/state final List parts = topic.split('/'); if (parts.length != 4 || parts[1] != 'contact' || parts[3] != 'state') { continue; } final String address = parts[2]; final bool? parsedState = _parseBool(payload); if (parsedState != null) { contacts[address] = parsedState; notifyListeners(); } } } bool? _parseBool(String payload) { switch (payload.toLowerCase()) { case 'open': return true; case 'closed': return false; default: return null; } } }