Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ViewThingViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
15using System.Collections.ObjectModel;
16using System.ComponentModel;
17using System.Globalization;
18using System.Text;
28using Waher.Things;
29
31{
35 public partial class ViewThingViewModel : QrXmppViewModel
36 {
37
38 private readonly IAuthenticationService authenticationService = ServiceRef.Provider.GetRequiredService<IAuthenticationService>();
39 private readonly Dictionary<string, PresenceEventArgs> presences = new(StringComparer.InvariantCultureIgnoreCase);
40 private readonly ViewThingNavigationArgs? navigationArguments;
41 private readonly ContactInfo? thing;
42
48 : base()
49 {
50 this.navigationArguments = Args;
51 this.thing = Args?.Thing;
52
53 this.Tags = [];
54 this.Notifications = [];
55
56 if (this.thing?.MetaData is not null)
57 {
58 foreach (Property Tag in this.thing.MetaData)
59 this.Tags.Add(new HumanReadableTag(Tag));
60 }
61
62 this.InContacts = !string.IsNullOrEmpty(this.thing?.ObjectId);
63 this.IsOwner = this.thing?.Owner ?? false;
64 this.IsSensor = this.thing?.IsSensor ?? false;
65 this.IsActuator = this.thing?.IsActuator ?? false;
66 this.IsConcentrator = this.thing?.IsConcentrator ?? false;
67 this.IsNodeInConcentrator = !string.IsNullOrEmpty(this.thing?.NodeId) || !string.IsNullOrEmpty(this.thing?.SourceId) || !string.IsNullOrEmpty(this.thing?.Partition);
68 this.SupportsSensorEvents = this.thing?.SupportsSensorEvents ?? false;
69
70 this.InContactsAndNotOwner = this.InContacts && !this.IsOwner;
71 this.NotInContacts = !this.InContacts;
72 this.IsConnectedAndOwner = this.IsConnected && this.IsOwner;
73 this.IsConnectedAndSensor = this.IsConnected && this.IsSensor;
74 this.IsConnectedAndActuator = this.IsConnected && this.IsActuator;
75 this.IsConnectedAndNotConcentrator = this.IsConnected && !this.IsConcentrator;
76 }
77
79 public override async Task OnInitializeAsync()
80 {
81 await base.OnInitializeAsync();
82
83 if (this.navigationArguments?.Events is not null)
84 {
85 this.Notifications.Clear();
86
87 int c = 0;
88
89 foreach (NotificationEvent Event in this.navigationArguments.Events)
90 {
91 this.Notifications.Add(new EventModel(Event.Received,
92 await Event.GetCategoryIcon(),
93 await Event.GetDescription(),
94 Event));
95
96 if (Event.Type == NotificationEventType.Contacts)
97 c++;
98 }
99
100 this.NrPendingChatMessages = c;
101 this.HasPendingChatMessages = c > 0;
102 }
103
104 this.HasNotifications = this.Notifications.Count > 0;
105
106 await this.CalcThingIsOnline();
107
108 ServiceRef.XmppService.OnPresence += this.Xmpp_OnPresence;
109 ServiceRef.XmppService.OnRosterItemAdded += this.Xmpp_OnRosterItemAdded;
110 ServiceRef.XmppService.OnRosterItemUpdated += this.Xmpp_OnRosterItemUpdated;
111 ServiceRef.XmppService.OnRosterItemRemoved += this.Xmpp_OnRosterItemRemoved;
112 ServiceRef.TagProfile.Changed += this.TagProfile_Changed;
113 ServiceRef.NotificationService.OnNewNotification += this.NotificationService_OnNewNotification;
114 ServiceRef.NotificationService.OnNotificationsDeleted += this.NotificationService_OnNotificationsDeleted;
115
116 if (this.IsConnected && this.IsThingOnline)
117 await this.CheckCapabilities();
118
119 this.GenerateQrCode(this.Link);
120 }
121
122
123 protected override Task XmppService_ConnectionStateChanged(object? _, XmppState NewState)
124 {
125 base.XmppService_ConnectionStateChanged(_, NewState);
126
127 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
128
129 return Task.CompletedTask;
130 }
131
132 private async Task CheckCapabilities()
133 {
134 if (this.InContacts &&
135 this.thing is not null &&
136 (!this.thing.IsSensor.HasValue ||
137 !this.thing.IsActuator.HasValue ||
138 !this.thing.IsConcentrator.HasValue ||
139 !this.thing.SupportsSensorEvents.HasValue))
140 {
141 string? FullJid = this.GetFullJid();
142
143 if (!string.IsNullOrEmpty(FullJid))
144 {
145 try
146 {
147 ServiceDiscoveryEventArgs e = await ServiceRef.XmppService.SendServiceDiscoveryRequest(FullJid);
148 if (!this.InContacts)
149 return;
150
151 this.thing.IsSensor = e.HasAnyFeature(SensorClient.NamespacesSensorData);
152 this.thing.SupportsSensorEvents = e.HasAnyFeature(SensorClient.NamespacesSensorEvents);
153 this.thing.IsActuator = e.HasAnyFeature(ControlClient.NamespacesControl);
154 this.thing.IsConcentrator = e.HasAnyFeature(ConcentratorServer.NamespacesConcentrator);
155
156 if (this.InContacts && !string.IsNullOrEmpty(this.thing.ObjectId))
157 await Database.Update(this.thing);
158
159 MainThread.BeginInvokeOnMainThread(() =>
160 {
161 this.IsSensor = this.thing.IsSensor ?? false;
162 this.IsActuator = this.thing.IsActuator ?? false;
163 this.IsConcentrator = this.thing.IsConcentrator ?? false;
164 this.SupportsSensorEvents = this.thing.SupportsSensorEvents ?? false;
165 });
166 }
167 catch (Exception ex)
168 {
169 ServiceRef.LogService.LogException(ex);
170 }
171 }
172 }
173 }
174
176 public override async Task OnDisposeAsync()
177 {
178 ServiceRef.XmppService.OnPresence -= this.Xmpp_OnPresence;
179 ServiceRef.XmppService.OnRosterItemAdded -= this.Xmpp_OnRosterItemAdded;
180 ServiceRef.XmppService.OnRosterItemUpdated -= this.Xmpp_OnRosterItemUpdated;
181 ServiceRef.XmppService.OnRosterItemRemoved -= this.Xmpp_OnRosterItemRemoved;
182 ServiceRef.TagProfile.Changed -= this.TagProfile_Changed;
183 ServiceRef.NotificationService.OnNewNotification -= this.NotificationService_OnNewNotification;
184 ServiceRef.NotificationService.OnNotificationsDeleted -= this.NotificationService_OnNotificationsDeleted;
185
186 await base.OnDisposeAsync();
187 }
188
189 private async Task Xmpp_OnPresence(object? Sender, PresenceEventArgs e)
190 {
191 switch (e.Type)
192 {
193 case PresenceType.Available:
194 this.presences[e.FromBareJID] = e;
195
196 if (!this.InContacts && string.Equals(e.FromBareJID, this.thing?.BareJid, StringComparison.OrdinalIgnoreCase))
197 {
198 if (string.IsNullOrEmpty(this.thing?.ObjectId))
199 await Database.Insert(this.thing);
200
201 MainThread.BeginInvokeOnMainThread(() =>
202 {
203 this.InContacts = true;
204 });
205 }
206
207 await this.CalcThingIsOnline();
208
209 break;
210
211 case PresenceType.Unavailable:
212 this.presences.Remove(e.FromBareJID);
213 break;
214 }
215 }
216
217 private async Task CalcThingIsOnline()
218 {
219
220 if (this.thing is null)
221 MainThread.BeginInvokeOnMainThread(() => this.IsThingOnline = false);
222 else
223 {
224 try
225 {
226 await MainThread.InvokeOnMainThreadAsync(() => this.IsThingOnline = this.IsOnline(this.thing.BareJid));
227 if (this.IsThingOnline)
228 await this.CheckCapabilities();
229 }
230 catch (Exception ex)
231 {
232 ServiceRef.LogService.LogException(ex);
234 }
235
236 }
237 }
238
239 async partial void OnInContactsChanged(bool value)
240 {
241 await this.CalcThingIsOnline();
242 }
243 private bool IsOnline(string BareJid)
244 {
245 if (this.presences.TryGetValue(BareJid, out PresenceEventArgs? e))
246 return e.IsOnline;
247 try
248 {
249 RosterItem? Item = ServiceRef.XmppService?.GetRosterItem(BareJid);
250 if (Item is not null && Item.HasLastPresence)
251 return Item.LastPresence.IsOnline;
252 }
253 catch (Exception)
254 {
255 //ignore
256 }
257
258 return false;
259 }
260
261 private string? GetFullJid()
262 {
263 if (this.thing is null)
264 return null;
265 else
266 {
267 if (this.presences.TryGetValue(this.thing.BareJid, out PresenceEventArgs? e))
268 return (e?.IsOnline == true) ? e.From : null;
269
270 try
271 {
272 RosterItem? Item = ServiceRef.XmppService.GetRosterItem(this.thing.BareJid);
273
274 if (Item is null || !Item.HasLastPresence || !Item.LastPresence.IsOnline)
275 return null;
276 else
277 return Item.LastPresenceFullJid;
278 }
279 catch (Exception)
280 {
281 return null;
282 }
283
284 }
285 }
286
287 private void TagProfile_Changed(object? Sender, PropertyChangedEventArgs e)
288 {
289 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
290 }
291
292 #region Properties
293
297 public ObservableCollection<HumanReadableTag> Tags { get; }
298
302 public ObservableCollection<EventModel> Notifications { get; }
303
307 [ObservableProperty]
308 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
309 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
310 private bool inContacts;
311
315 [ObservableProperty]
316 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
317 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
318 private bool notInContacts;
319
320
321 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
322 {
323 base.OnPropertyChanged(e);
324 MainThread.BeginInvokeOnMainThread(() =>
325 {
326
327 switch (e.PropertyName)
328 {
329 case nameof(this.IsBusy):
330 this.ReadSensorCommand.NotifyCanExecuteChanged();
331 this.ControlActuatorCommand.NotifyCanExecuteChanged();
332 this.ChatCommand.NotifyCanExecuteChanged();
333 break;
334 }
335
336 //This looks a bit cursed
337 switch (e.PropertyName)
338 {
339 case nameof(this.InContacts):
340 case nameof(this.IsOwner):
341 this.InContactsAndNotOwner = this.InContacts && !this.IsOwner;
342 this.NotInContacts = !this.InContacts;
343 break;
344 }
345
346 switch (e.PropertyName)
347 {
348 case nameof(this.IsConnected):
349 case nameof(this.IsOwner):
350 this.IsConnectedAndOwner = this.IsConnected && this.IsOwner;
351 break;
352 }
353
354 switch (e.PropertyName)
355 {
356 case nameof(this.IsConnected):
357 case nameof(this.IsSensor):
358 this.IsConnectedAndSensor = this.IsConnected && this.IsSensor;
359 break;
360 }
361
362 switch (e.PropertyName)
363 {
364 case nameof(this.IsConnected):
365 case nameof(this.IsActuator):
366 this.IsConnectedAndActuator = this.IsConnected && this.IsActuator;
367 break;
368 }
369
370 switch (e.PropertyName)
371 {
372 case nameof(this.IsConnected):
373 case nameof(this.IsConcentrator):
374 this.IsConnectedAndNotConcentrator = this.IsConnected && !this.IsConcentrator;
375 break;
376 }
377 });
378 }
379
380 public string? FriendlyName => this.thing?.FriendlyName ?? this.thing?.BareJid ?? string.Empty;
381
385 [ObservableProperty]
386 private bool isOwner;
387
391 [ObservableProperty]
392 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
393 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
394 private bool inContactsAndNotOwner;
395
399 [ObservableProperty]
400 [NotifyCanExecuteChangedFor(nameof(DeleteRulesCommand))]
401 [NotifyCanExecuteChangedFor(nameof(DisownThingCommand))]
402 private bool isConnectedAndOwner;
403
407 [ObservableProperty]
408 [NotifyCanExecuteChangedFor(nameof(ReadSensorCommand))]
409 private bool isConnectedAndSensor;
410
414 [ObservableProperty]
415 [NotifyCanExecuteChangedFor(nameof(ControlActuatorCommand))]
416 private bool isConnectedAndActuator;
417
421 [ObservableProperty]
422 private bool isConnectedAndNotConcentrator;
423
427 [ObservableProperty]
428 private bool isThingOnline;
429
433 [ObservableProperty]
434 [NotifyCanExecuteChangedFor(nameof(ReadSensorCommand))]
435 private bool isSensor;
436
440 [ObservableProperty]
441 [NotifyCanExecuteChangedFor(nameof(ControlActuatorCommand))]
442 private bool isActuator;
443
447 [ObservableProperty]
448 private bool isConcentrator;
449
453 [ObservableProperty]
454 private bool isNodeInConcentrator;
455
459 [ObservableProperty]
460 private bool supportsSensorEvents;
464 [ObservableProperty]
465 private bool hasNotifications;
466
470 [ObservableProperty]
471 private bool hasPendingChatMessages;
472
476 [ObservableProperty]
477 private int nrPendingChatMessages;
478
479 #endregion
480
484 [RelayCommand]
485 private static Task Click(object obj)
486 {
487 if (obj is HumanReadableTag Tag)
488 return ViewClaimThing.ViewClaimThingViewModel.LabelClicked(Tag.Name, Tag.Value, Tag.LocalizedValue);
489 else if (obj is string s)
490 return ViewClaimThing.ViewClaimThingViewModel.LabelClicked(string.Empty, s, s);
491 else
492 return Task.CompletedTask;
493 }
494
498 [RelayCommand]
499 private async Task CopyQr(object Item)
500 {
501 try
502 {
503 this.SetIsBusy(true);
504
505 await Clipboard.SetTextAsync(this.Link);
509 }
510 catch (Exception ex)
511 {
512 ServiceRef.LogService.LogException(ex);
514 }
515 finally
516 {
517 this.SetIsBusy(false);
518 }
519 }
520
524 [RelayCommand(CanExecute = nameof(IsConnectedAndOwner))]
525 private async Task DeleteRules()
526 {
527 if (this.thing is null)
528 return;
529
530 try
531 {
535 {
536 return;
537 }
538
539 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.DeleteRules, true))
540 return;
541
542 TaskCompletionSource<bool> Result = new();
543
544 ServiceRef.XmppService.DeleteDeviceRules(this.thing.RegistryJid, this.thing.BareJid, this.thing.NodeId,
545 this.thing.SourceId, this.thing.Partition, (sender, e) =>
546 {
547 if (e.Ok)
548 Result.TrySetResult(true);
549 else if (e.StanzaError is not null)
550 Result.TrySetException(e.StanzaError);
551 else
552 Result.TrySetResult(false);
553
554 return Task.CompletedTask;
555 }, null);
556
557
558 if (!await Result.Task)
559 return;
560
563 }
564 catch (Exception ex)
565 {
566 ServiceRef.LogService.LogException(ex);
568 }
569 }
570
574 [RelayCommand(CanExecute = nameof(IsConnectedAndOwner))]
575 private async Task DisownThing()
576 {
577 if (this.thing is null)
578 return;
579
580 try
581 {
585 {
586 return;
587 }
588
589 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.DisownThing, true))
590 return;
591
592 (bool Succeeded, bool Done) = await ServiceRef.NetworkService.TryRequest(() =>
593 ServiceRef.XmppService.Disown(this.thing.RegistryJid, this.thing.BareJid, this.thing.SourceId, this.thing.Partition, this.thing.NodeId));
594
595 if (!Succeeded)
596 return;
597
598 if (this.InContacts)
599 {
600 if (!string.IsNullOrEmpty(this.thing?.ObjectId))
601 {
602 await Database.Delete(this.thing);
603 await Database.Provider.Flush();
604
605 this.thing.ObjectId = null;
606 }
607
608 if (this.thing is not null)
609 this.thing.ObjectId = null;
610
611 this.InContacts = false;
612 }
613
616 await this.GoBack();
617 }
618 catch (Exception ex)
619 {
620 ServiceRef.LogService.LogException(ex);
622 }
623 }
624
628 [RelayCommand(CanExecute = nameof(NotInContacts))]
629 private async Task AddToList()
630 {
631 if (this.thing is null)
632 return;
633 try
634 {
635 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.AddToListOfThings))
636 return;
637
638 RosterItem? Item = ServiceRef.XmppService.GetRosterItem(this.thing.BareJid);
639 if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From)
640 {
641 string IdXml;
642
643 if (ServiceRef.TagProfile.LegalIdentity is null)
644 IdXml = string.Empty;
645 else
646 {
647 StringBuilder Xml = new();
648 ServiceRef.TagProfile.LegalIdentity.Serialize(Xml, true, true, true, true, true, true, true);
649 IdXml = Xml.ToString();
650 }
651 ServiceRef.XmppService.RequestPresenceSubscription(this.thing.BareJid);
653 MainThread.BeginInvokeOnMainThread(() => this.NotInContacts = false);
654 }
655 await MainThread.InvokeOnMainThreadAsync(async () =>
656 {
657 if (!this.InContacts)
658 {
659 if (string.IsNullOrEmpty(this.thing.ObjectId))
660 await Database.Insert(this.thing);
661
662 this.InContacts = true;
663 }
664
665 await this.CalcThingIsOnline();
666 });
667
668 }
669 catch (Exception ex)
670 {
671 ServiceRef.LogService.LogException(ex);
673 }
674 }
675
679 [RelayCommand(CanExecute = nameof(InContactsAndNotOwner))]
680 private async Task RemoveFromList()
681 {
682 if (this.thing is null)
683 return;
684
685 try
686 {
687 if (!await this.authenticationService.AuthenticateUserAsync(AuthenticationPurpose.RemoveFromListOfThings))
688 return;
689
690 if (this.InContacts)
691 {
692 if (!string.IsNullOrEmpty(this.thing.ObjectId))
693 {
694 await Database.Delete(this.thing);
695 this.thing.ObjectId = null;
696 }
697
698 ServiceRef.XmppService.RequestPresenceUnsubscription(this.thing.BareJid);
699
700 if (ServiceRef.XmppService.GetRosterItem(this.thing.BareJid) is not null)
701 ServiceRef.XmppService.RemoveRosterItem(this.thing.BareJid);
702
703 await this.GoBack();
704 }
705 }
706 catch (Exception ex)
707 {
708 ServiceRef.LogService.LogException(ex);
710 }
711 }
712 private bool CanReadSensor => !this.IsBusy && this.IsConnectedAndSensor;
713
717 [RelayCommand(CanExecute = nameof(CanReadSensor))]
718 private async Task ReadSensor()
719 {
720 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
721
722 if (this.thing is null)
723 return;
724
725 ViewThingNavigationArgs Args = new(this.thing, MyThingsViewModel.GetNotificationEvents(this.thing) ?? []);
726
728
729 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(false));
730
731 }
732
733 private bool CanControlActuator => !this.IsBusy && this.IsConnectedAndActuator;
734
738 [RelayCommand(CanExecute = nameof(CanControlActuator))]
739 private async Task ControlActuator()
740 {
741 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
742
743 if (this.thing is null)
744 return;
745
746 try
747 {
748 string? FullJid = this.GetFullJid();
749 if (string.IsNullOrEmpty(FullJid))
750 return;
751
752 LanguageInfo SelectedLanguage = App.SelectedLanguage;
753
754 if (string.IsNullOrEmpty(this.thing.NodeId) && string.IsNullOrEmpty(this.thing.SourceId) && string.IsNullOrEmpty(this.thing.Partition))
755 ServiceRef.XmppService.GetControlForm(FullJid, SelectedLanguage.Name, this.ControlFormCallback, null);
756 else
757 {
758 ThingReference ThingRef = new(this.thing.NodeId, this.thing.SourceId, this.thing.Partition);
759 ServiceRef.XmppService.GetControlForm(FullJid, SelectedLanguage.Name, this.ControlFormCallback, null, ThingRef);
760 }
761 }
762 catch (Exception ex)
763 {
765 }
766 }
767
768 private Task ControlFormCallback(object? Sender, DataFormEventArgs e)
769 {
770 if (e.Ok)
771 {
772 MainThread.BeginInvokeOnMainThread(async () =>
773 {
774 await ServiceRef.NavigationService.GoToAsync(nameof(XmppFormPage), new XmppFormNavigationArgs(e.Form));
775 });
776 }
777 else
778 {
779 ServiceRef.UiService.DisplayException(e.StanzaError ?? new Exception("Unable to get control form."));
780 }
781 MainThread.BeginInvokeOnMainThread(() => this.SetIsBusy(false));
782 return Task.CompletedTask;
783 }
784
785
786 private bool CanChat => !this.IsBusy && this.IsConnectedAndNotConcentrator;
787
791 [RelayCommand(CanExecute = nameof(CanChat))]
792 private async Task Chat()
793 {
794 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
795
796 if (this.thing is null)
797 return;
798
799 try
800 {
801 string LegalId = this.thing.LegalId;
802 string FriendlyName = this.thing.FriendlyName;
803 ChatNavigationArgs Args = new(LegalId, this.thing.BareJid, FriendlyName);
804
805 await ServiceRef.NavigationService.GoToAsync(nameof(ChatPage), Args, BackMethod.Inherited, this.thing.BareJid);
806 }
807 catch (Exception ex)
808 {
810 }
811 finally
812 {
813 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(false));
814
815 }
816 }
817
818 private Task NotificationService_OnNotificationsDeleted(object? Sender, NotificationEventsArgs e)
819 {
820 if (this.thing is not null)
821 {
822 MainThread.BeginInvokeOnMainThread(() =>
823 {
824 bool IsNode = this.IsNodeInConcentrator;
825 string Key = this.thing.ThingNotificationCategoryKey;
826 int NrChatMessagesRemoved = 0;
827
828 foreach (NotificationEvent Event in e.Events)
829 {
830 switch (Event.Type)
831 {
832 case NotificationEventType.Contacts:
833 if (IsNode)
834 continue;
835
836 if (Event.Category != this.thing.BareJid)
837 continue;
838 break;
839
840 case NotificationEventType.Things:
841 if (Event.Category != Key)
842 continue;
843 break;
844
845 default:
846 continue;
847 }
848
849 int i = 0;
850
851 foreach (EventModel Model in this.Notifications)
852 {
853 if (Model.Event.ObjectId == Event.ObjectId)
854 {
855 this.Notifications.RemoveAt(i);
856
857 if (Event.Type == NotificationEventType.Contacts)
858 NrChatMessagesRemoved++;
859
860 break;
861 }
862
863 i++;
864 }
865 }
866
867 this.NrPendingChatMessages -= NrChatMessagesRemoved;
868 this.HasNotifications = this.Notifications.Count > 0;
869 this.HasPendingChatMessages = this.NrPendingChatMessages > 0;
870 });
871 }
872
873 return Task.CompletedTask;
874 }
875
876 private Task NotificationService_OnNewNotification(object? Sender, NotificationEventArgs e)
877 {
878 MainThread.BeginInvokeOnMainThread(async () =>
879 {
880 try
881 {
882 await this.CalcThingIsOnline();
883
884 switch (e.Event.Type)
885 {
886 case NotificationEventType.Contacts:
887 if (this.IsNodeInConcentrator)
888 return;
889
890 if (e.Event.Category != this.thing?.BareJid)
891 return;
892 break;
893
894 case NotificationEventType.Things:
895 if (e.Event.Category != this.thing?.ThingNotificationCategoryKey)
896 return;
897 break;
898
899 default:
900 return;
901 }
902
903 this.Notifications.Add(new EventModel(e.Event.Received,
904 await e.Event.GetCategoryIcon(),
905 await e.Event.GetDescription(),
906 e.Event));
907
908 this.HasNotifications = true;
909
910 if (e.Event.Type == NotificationEventType.Contacts)
911 {
912 this.NrPendingChatMessages++;
913 this.HasPendingChatMessages = true;
914 }
915 }
916 catch (Exception ex)
917 {
918 ServiceRef.LogService.LogException(ex);
919 }
920 });
921
922 return Task.CompletedTask;
923 }
924
925 private Task Xmpp_OnRosterItemRemoved(object? Sender, RosterItem Item)
926 {
927 this.presences.Remove(Item.BareJid);
928 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
929 return Task.CompletedTask;
930 }
931
932 private Task Xmpp_OnRosterItemUpdated(object? Sender, RosterItem Item)
933 {
934 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
935 return Task.CompletedTask;
936 }
937
938 private Task Xmpp_OnRosterItemAdded(object? Sender, RosterItem Item)
939 {
940 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
941 return Task.CompletedTask;
942 }
943
944 #region ILinkableView
945
949 public override bool IsLinkable => true;
950
954 public override bool EncodeAppLinks => true;
955
959 public override string Link
960 {
961 get
962 {
963 StringBuilder sb = new();
964 bool HasJid = false;
965 bool HasSourceId = false;
966 bool HasPartition = false;
967 bool HasNodeId = false;
968 bool HasRegistry = false;
969
970 sb.Append("iotdisco:");
971
972 if (this.thing is not null)
973 {
974 if (this.thing.MetaData is not null)
975 {
976 foreach (Property P in this.thing.MetaData)
977 {
978
979 switch (P.Name.ToUpper(CultureInfo.InvariantCulture))
980 {
985 sb.Append('#');
986 break;
987
989 HasJid = true;
990 break;
991
993 HasSourceId = true;
994 break;
995
997 HasPartition = true;
998 break;
999
1001 HasNodeId = true;
1002 break;
1003
1005 HasRegistry = true;
1006 break;
1007 }
1008 sb.Append(Uri.EscapeDataString(P.Name));
1009 sb.Append('=');
1010 sb.Append(Uri.EscapeDataString(P.Value));
1011 sb.Append(';');
1012
1013 }
1014 }
1015
1016 if (!HasJid)
1017 {
1018 sb.Append("JID=");
1019 sb.Append(Uri.EscapeDataString(this.thing.BareJid));
1020 }
1021
1022 if (!HasSourceId && !string.IsNullOrEmpty(this.thing.SourceId))
1023 {
1024 sb.Append(";SID=");
1025 sb.Append(Uri.EscapeDataString(this.thing.SourceId));
1026 }
1027
1028 if (!HasPartition && !string.IsNullOrEmpty(this.thing.Partition))
1029 {
1030 sb.Append(";PT=");
1031 sb.Append(Uri.EscapeDataString(this.thing.Partition));
1032 }
1033
1034 if (!HasNodeId && !string.IsNullOrEmpty(this.thing.NodeId))
1035 {
1036 sb.Append(";NID=");
1037 sb.Append(Uri.EscapeDataString(this.thing.NodeId));
1038 }
1039
1040 if (!HasRegistry && !string.IsNullOrEmpty(this.thing.RegistryJid))
1041 {
1042 sb.Append(";R=");
1043 sb.Append(Uri.EscapeDataString(this.thing.RegistryJid));
1044 }
1045 }
1046
1047 return sb.ToString();
1048 }
1049 }
1050
1054 public override Task<string> Title => Task.FromResult(this.thing?.FriendlyName ?? string.Empty);
1055
1059 public override bool HasMedia => false;
1060
1064 public override byte[]? Media => null;
1065
1069 public override string? MediaContentType => null;
1070
1071 #endregion
1072 }
1073}
Represents an instance of the Neuro-Access app.
Definition: App.xaml.cs:125
static LanguageInfo SelectedLanguage
Gets the selected language.
Definition: App.xaml.cs:199
A set of never changing property constants and helpful values.
Definition: Constants.cs:24
A strongly-typed resource class, for looking up localized strings, etc.
static string Yes
Looks up a localized string similar to Yes.
static string Cancel
Looks up a localized string similar to Cancel.
static string ThingDisowned
Looks up a localized string similar to This has been successfully disowned. It can now be claimed by ...
static string Question
Looks up a localized string similar to Question.
static string DeleteRulesQuestion
Looks up a localized string similar to Do you want to delete all provisioning rules for the thing?...
static string ARequestHasBeenSentToTheOwner
Looks up a localized string similar to A request has been sent to the owner of the device....
static string RulesDeleted
Looks up a localized string similar to Provisioning rules have been deleted..
static string DisownThingQuestion
Looks up a localized string similar to Do you want to disown the thing? This will remove the thing fr...
static string SuccessTitle
Looks up a localized string similar to Success.
Contains information about a contact.
Definition: ContactInfo.cs:22
Base class that references services in the app.
Definition: ServiceRef.cs:43
static IServiceProvider Provider
The service provider for the app. This is set before the app is started, and will be used to resolve ...
Definition: ServiceRef.cs:48
static ILogService LogService
Log service.
Definition: ServiceRef.cs:214
static INetworkService NetworkService
Network service.
Definition: ServiceRef.cs:226
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:130
static INavigationService NavigationService
The navigation service for navigating between pages.
Definition: ServiceRef.cs:178
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:202
static IReportingStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:370
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:190
virtual async Task GoBack()
Method called when user wants to navigate to the previous screen.
virtual void SetIsBusy(bool IsBusy)
Sets the IsBusy property.
Holds navigation parameters specific to views displaying a list of contacts.
A page that displays a list of the current user's contacts.
A page that displays an XMPP Form to the user.
A view model that holds the XMPP state.
void GenerateQrCode(string Uri)
Generates a QR-code
Class used to present a meta-data tag in a human interface.
A page that displays sensor data from a sensor.
Holds navigation parameters specific to viewing things.
The view model to bind to when displaying a thing.
override async Task OnDisposeAsync()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
ViewThingViewModel(ViewThingNavigationArgs? Args)
Creates an instance of the ViewThingViewModel class.
override Task XmppService_ConnectionStateChanged(object? _, XmppState NewState)
Listens to connection state changes from the XMPP server.
override async Task OnInitializeAsync()
Method called when view is initialized for the first time. Use this method to implement registration ...
ObservableCollection< EventModel > Notifications
Holds a list of notifications.
ObservableCollection< HumanReadableTag > Tags
Holds a list of meta-data tags associated with a thing.
Implements an XMPP concentrator server interface.
static readonly string[] NamespacesConcentrator
Supported concentrator namespaces.
Implements an XMPP control client interface.
static readonly string[] NamespacesControl
Supported control namespaces
Event arguments for data form results.
Class containing information about media content in a data form.
Definition: Media.cs:18
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppException StanzaError
Any stanza error returned.
Event arguments for presence events.
string FromBareJID
Bare JID of resource sending the presence.
PresenceType Type
Type of presence received.
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
Implements an XMPP sensor client interface.
Definition: SensorClient.cs:21
static readonly string[] NamespacesSensorEvents
Supported sensor event namespaces.
Definition: SensorClient.cs:64
static readonly string[] NamespacesSensorData
Supported sensor-data namespaces.
Definition: SensorClient.cs:40
Contains information about an item of an entity.
Definition: Item.cs:11
bool HasAnyFeature(params string[] Features)
Checks if the remote entity supports any of a set of features.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:21
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:59
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
Contains a reference to a thing
Task GoToAsync(string Route)
Navigates to the specified route and pushes the page onto the navigation stack.
Task DisplayException(Exception Exception, string? Title=null)
Displays an alert/message box to the user.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user.
Task Flush()
Persists any pending changes.
Definition: ImplTypes.g.cs:58
abstract class NotificationEvent()
Abstract base class of notification events.
class NotificationEventsArgs(NotificationEvent[] Events)
Event argument for notification events.
class NotificationEventArgs(NotificationEvent Event)
Event argument for notification events.
NotificationEventType
Button on which event is to be displayed.
BackMethod
Navigation Back Method
Definition: BackMethod.cs:7
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
Notifications
Determines if the resource is observable, and how notifications are sent.
Definition: CoapResource.cs:15
PresenceType
Type of presence received.
Definition: PresenceType.cs:7
SubscriptionState
State of a presence subscription.
Definition: RosterItem.cs:16
XmppState
State of XMPP connection.
Definition: XmppState.cs:7