diff options
| author | xengineering <me@xengineering.eu> | 2026-01-15 21:25:45 +0100 |
|---|---|---|
| committer | xengineering <me@xengineering.eu> | 2026-01-15 21:25:45 +0100 |
| commit | 7cdfe307fa3ae2de5326321cae74495fe0680d46 (patch) | |
| tree | d6bef0ac48e6e328a04df27a42f45f5c77268bb3 | |
| parent | 38b49eb626ab0b3fdd634e82d21dacc593dd6629 (diff) | |
| download | sia-app-7cdfe307fa3ae2de5326321cae74495fe0680d46.tar sia-app-7cdfe307fa3ae2de5326321cae74495fe0680d46.tar.zst sia-app-7cdfe307fa3ae2de5326321cae74495fe0680d46.zip | |
Set contact states based on MQTT
This is already the minimal viable product (MVP) for this app.
| -rw-r--r-- | lib/data.dart | 86 |
1 files changed, 65 insertions, 21 deletions
diff --git a/lib/data.dart b/lib/data.dart index f8b7466..08f8881 100644 --- a/lib/data.dart +++ b/lib/data.dart @@ -1,43 +1,87 @@ import 'dart:async'; 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'; class AppState with ChangeNotifier { - Map<String, bool> contacts = <String, bool>{ - 'Living Room Window': false, - 'Front Door': true, - 'Back Door': false, - 'Garage Window': true, - }; + Map<String, bool> contacts = <String, bool>{}; - Timer? _timer; + late final MqttServerClient _client; AppState() { - _timer = Timer.periodic(const Duration(seconds: 1), (_) { - toggleFirstContact(); - }); + _initMqtt(); } - void toggleFirstContact() { - if (contacts.isEmpty) { + @override + void dispose() { + _client.disconnect(); + super.dispose(); + } + + Future<void> _initMqtt() async { + _client = MqttServerClient( + brokerHostname, + 'sia_app_${DateTime.now().millisecondsSinceEpoch}', + ); + + _client.port = brokerPort; + _client.keepAlivePeriod = 2; + _client.logging(on: false); + + _client.onConnected = _onConnected; + + try { + await _client.connect(); + } catch (e) { + _client.disconnect(); return; } - const String choosen = 'Living Room Window'; - bool? state = contacts[choosen]; - if (state != null) { - contacts[choosen] = !state; - notifyListeners(); + _client.updates?.listen(_onMessage); + } + + void _onConnected() { + _client.subscribe('$topicPrefix/contact/+/state', MqttQos.exactlyOnce); + } + + void _onMessage(List<MqttReceivedMessage<MqttMessage>> messages) { + for (final MqttReceivedMessage<MqttMessage> message in messages) { + final String topic = message.topic; + + // format <prefix>/contacts/<address>/state + final List<String> parts = topic.split('/'); + if (parts.length != 4 || parts[1] != 'contact' || parts[3] != 'state') { + continue; + } + final String address = parts[2]; + + final MqttPublishMessage payloadMessage = + message.payload as MqttPublishMessage; + final String payload = + MqttPublishPayload.bytesToStringAsString(payloadMessage.payload.message); + + final bool? parsedState = _parseBool(payload); + + if (parsedState != null) { + contacts[address] = parsedState; + notifyListeners(); + } } } - @override - void dispose() { - _timer?.cancel(); - super.dispose(); + bool? _parseBool(String payload) { + switch (payload.toLowerCase()) { + case 'open': + return true; + case 'closed': + return false; + default: + return null; + } } } |
