summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2026-01-15 21:25:45 +0100
committerxengineering <me@xengineering.eu>2026-01-15 21:25:45 +0100
commit7cdfe307fa3ae2de5326321cae74495fe0680d46 (patch)
treed6bef0ac48e6e328a04df27a42f45f5c77268bb3
parent38b49eb626ab0b3fdd634e82d21dacc593dd6629 (diff)
downloadsia-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.dart86
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;
+ }
}
}