├── .gitattributes ├── .gitignore ├── AppToNotifyUsers.sln ├── CONTRIBUTING.md ├── LICENSE ├── WorkerHost ├── App.config ├── ConfigurationLoader.cs ├── EventHubReader.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── WorkerHost.csproj └── packages.config ├── WorkerRole ├── ServiceConfiguration.Cloud.cscfg ├── ServiceConfiguration.Local.cscfg ├── ServiceDefinition.csdef ├── WorkerHostContent │ └── diagnostics.wadcfgx └── WorkerRole.ccproj ├── contribute.md ├── event-hubs-erp-alert.png ├── event-hubs-sensor-alert.png ├── event-hubs-sensors-notify-users.md └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | # Prevent from further automatic changes 5 | * text=binary 6 | 7 | ############################################################################### 8 | # We assume following configuration 9 | # Some sh files and other could be changed from Windows too 10 | ############################################################################### 11 | #* text=auto 12 | #*.sln text eol=crlf 13 | #*.csproj text eol=crlf 14 | #*.cs text eol=crlf 15 | #*.cpp text eol=crlf 16 | #*.h text eol=crlf 17 | #*.config text eol=crlf 18 | #*.sql text eol=crlf 19 | #*.asax text eol=crlf 20 | #*.css text eol=crlf 21 | #*.js text eol=crlf 22 | #*.xsd text eol=crlf 23 | # 24 | #*.wxs text eol=crlf 25 | #*.wixproj text eol=crlf 26 | # 27 | #*.cmd text eol=crlf 28 | # 29 | #*.ino text eol=lf 30 | #*.py text eol=lf 31 | #*.sh text eol=lf 32 | # 33 | ############################################################################### 34 | 35 | ############################################################################### 36 | # Set default behavior for command prompt diff. 37 | # 38 | # This is need for earlier builds of msysgit that does not have it on by 39 | # default for csharp files. 40 | # Note: This is only used by command line 41 | ############################################################################### 42 | #*.cs diff=csharp 43 | 44 | ############################################################################### 45 | # Set the merge driver for project and solution files 46 | # 47 | # Merging from the command prompt will add diff markers to the files if there 48 | # are conflicts (Merging from VS is not affected by the settings below, in VS 49 | # the diff markers are never inserted). Diff markers may cause the following 50 | # file extensions to fail to load in VS. An alternative would be to treat 51 | # these files as binary and thus will always conflict and require user 52 | # intervention with every merge. To do so, just uncomment the entries below 53 | ############################################################################### 54 | #*.sln merge=binary 55 | #*.csproj merge=binary 56 | #*.vbproj merge=binary 57 | #*.vcxproj merge=binary 58 | #*.vcproj merge=binary 59 | #*.dbproj merge=binary 60 | #*.fsproj merge=binary 61 | #*.lsproj merge=binary 62 | #*.wixproj merge=binary 63 | #*.modelproj merge=binary 64 | #*.sqlproj merge=binary 65 | #*.wwaproj merge=binary 66 | 67 | ############################################################################### 68 | # behavior for image files 69 | # 70 | # image files are treated as binary by default. 71 | ############################################################################### 72 | #*.jpg binary 73 | #*.png binary 74 | #*.gif binary 75 | 76 | ############################################################################### 77 | # diff behavior for common document formats 78 | # 79 | # Convert binary document formats to text before diffing them. This feature 80 | # is only available from the command line. Turn it on by uncommenting the 81 | # entries below. 82 | ############################################################################### 83 | #*.doc diff=astextplain 84 | #*.DOC diff=astextplain 85 | #*.docx diff=astextplain 86 | #*.DOCX diff=astextplain 87 | #*.dot diff=astextplain 88 | #*.DOT diff=astextplain 89 | #*.pdf diff=astextplain 90 | #*.PDF diff=astextplain 91 | #*.rtf diff=astextplain 92 | #*.RTF diff=astextplain 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | AppToNotifyUsers.sln.ide/ 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # NuGet Packages Directory 83 | packages 84 | 85 | # Windows Azure Build Output 86 | csx 87 | *.build.csdef 88 | 89 | # Windows Store app package directory 90 | AppPackages/ 91 | 92 | # Others 93 | [Bb]in 94 | [Oo]bj 95 | sql 96 | TestResults 97 | [Tt]est[Rr]esult* 98 | *.Cache 99 | ClientBin 100 | [Ss]tyle[Cc]op.* 101 | ~$* 102 | *.dbmdl 103 | Generated_Code #added for RIA/Silverlight projects 104 | 105 | # Backup & report files from converting an old project file to a newer 106 | # Visual Studio version. Backup files are not needed, because we have git ;-) 107 | _UpgradeReport_Files/ 108 | Backup*/ 109 | UpgradeLog*.XML 110 | 111 | # NuGet Packages 112 | *.nupkg 113 | .nuget 114 | 115 | #Thumbs.db 116 | Thumbs.db -------------------------------------------------------------------------------- /AppToNotifyUsers.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkerHost", "WorkerHost\WorkerHost.csproj", "{25079398-1602-45EE-837C-D4195A1FBC27}" 7 | EndProject 8 | Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "WorkerRole", "WorkerRole\WorkerRole.ccproj", "{73173D11-AA67-4089-91D5-BBE0BD4C5444}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {25079398-1602-45EE-837C-D4195A1FBC27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {25079398-1602-45EE-837C-D4195A1FBC27}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {25079398-1602-45EE-837C-D4195A1FBC27}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {25079398-1602-45EE-837C-D4195A1FBC27}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {73173D11-AA67-4089-91D5-BBE0BD4C5444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {73173D11-AA67-4089-91D5-BBE0BD4C5444}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {73173D11-AA67-4089-91D5-BBE0BD4C5444}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {73173D11-AA67-4089-91D5-BBE0BD4C5444}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Azure samples 2 | 3 | Thank you for your interest in contributing to Azure samples! 4 | 5 | ## Ways to contribute 6 | 7 | You can contribute to [Azure samples](https://azure.microsoft.com/documentation/samples/) in a few different ways: 8 | 9 | - Submit feedback on [this sample page](https://azure.microsoft.com/documentation/samples/event-hubs-dotnet-user-notifications/) whether it was helpful or not. 10 | - Submit issues through [issue tracker](https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications/issues) on GitHub. We are actively monitoring the issues and improving our samples. 11 | - If you wish to make code changes to samples, or contribute something new, please follow the [GitHub Forks / Pull requests model](https://help.github.com/articles/fork-a-repo/): Fork the sample repo, make the change and propose it back by submitting a pull request. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /WorkerHost/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /WorkerHost/ConfigurationLoader.cs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Corporation, Inc. All rights reserved. 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // --------------------------------------------------------------------------------- 24 | using System.Collections.Generic; 25 | using System.Configuration; 26 | 27 | 28 | namespace WorkerHost 29 | { 30 | public class AppConfiguration 31 | { 32 | public string DeviceSBConnectionString; 33 | public string DeviceEHName; 34 | 35 | 36 | public string NotificationService; 37 | public string EmailServiceUserName; 38 | public string EmailServicePassword; 39 | 40 | public string SmtpHost; 41 | public bool SmtpEnableSSL; 42 | 43 | public string MessageFromAddress; 44 | public string MessageFromName; 45 | public string MessageSubject; 46 | public string ConsumerGroupPrefix; 47 | 48 | public IList SendToList; 49 | } 50 | 51 | public class SendToConfigSection : ConfigurationSection 52 | { 53 | [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)] 54 | public SendToConfigInstanceCollection Instances 55 | { 56 | get { return (SendToConfigInstanceCollection)this[""]; } 57 | set { this[""] = value; } 58 | } 59 | } 60 | 61 | public class SendToConfigInstanceCollection : ConfigurationElementCollection 62 | { 63 | protected override ConfigurationElement CreateNewElement() 64 | { 65 | return new SendToConfigInstanceElement(); 66 | } 67 | 68 | protected override object GetElementKey(ConfigurationElement element) 69 | { 70 | return ((SendToConfigInstanceElement)element).Address; 71 | } 72 | } 73 | 74 | public class SendToConfigInstanceElement : ConfigurationElement 75 | { 76 | [ConfigurationProperty("address", IsKey = true, IsRequired = true)] 77 | public string Address 78 | { 79 | get 80 | { 81 | return (string)base["address"]; 82 | } 83 | } 84 | } 85 | 86 | internal class SendFromConfigSection : ConfigurationSection 87 | { 88 | [ConfigurationProperty("address", DefaultValue = "address", IsRequired = true)] 89 | public string Address 90 | { 91 | get 92 | { 93 | return (string)this["address"]; 94 | } 95 | set 96 | { 97 | this["address"] = value; 98 | } 99 | } 100 | 101 | [ConfigurationProperty("displayName", DefaultValue = "displayName", IsRequired = true)] 102 | public string DisplayName 103 | { 104 | get 105 | { 106 | return (string)this["displayName"]; 107 | } 108 | set 109 | { 110 | this["displayName"] = value; 111 | } 112 | } 113 | 114 | [ConfigurationProperty("subject", DefaultValue = "subject", IsRequired = true)] 115 | public string Subject 116 | { 117 | get 118 | { 119 | return (string)this["subject"]; 120 | } 121 | set 122 | { 123 | this["subject"] = value; 124 | } 125 | } 126 | } 127 | 128 | public static class ConfigurationLoader 129 | { 130 | public static IList GetSendToList() 131 | { 132 | var addresses = new List(); 133 | 134 | SendToConfigSection config = ConfigurationManager.GetSection("sendToList") as SendToConfigSection; 135 | 136 | if (config != null) 137 | { 138 | foreach (SendToConfigInstanceElement e in config.Instances) 139 | { 140 | addresses.Add(e.Address); 141 | } 142 | } 143 | 144 | return addresses; 145 | } 146 | 147 | public static AppConfiguration GetConfig() 148 | { 149 | SendFromConfigSection sendFromSection = (ConfigurationManager.GetSection("sendFrom") as SendFromConfigSection); 150 | 151 | if (sendFromSection == null) 152 | { 153 | return null; 154 | } 155 | 156 | AppConfiguration config = new AppConfiguration 157 | { 158 | DeviceSBConnectionString = 159 | ConfigurationManager.AppSettings.Get("Microsoft.ServiceBus.ServiceBusConnectionString"), 160 | DeviceEHName = ConfigurationManager.AppSettings.Get("Microsoft.ServiceBus.EventHubToMonitor"), 161 | NotificationService = ConfigurationManager.AppSettings.Get("NotificationService"), 162 | EmailServiceUserName = ConfigurationManager.AppSettings.Get("SenderUserName"), 163 | EmailServicePassword = ConfigurationManager.AppSettings.Get("SenderPassword"), 164 | SmtpHost = ConfigurationManager.AppSettings.Get("SmtpHost"), 165 | SmtpEnableSSL = ConfigurationManager.AppSettings.Get("SmtpEnableSSL").ToLowerInvariant() == "true", 166 | 167 | MessageFromAddress = sendFromSection.Address, 168 | MessageFromName = sendFromSection.DisplayName, 169 | MessageSubject = sendFromSection.Subject, 170 | 171 | SendToList = ConfigurationLoader.GetSendToList(), 172 | ConsumerGroupPrefix = ConfigurationManager.AppSettings.Get("ConsumerGroupPrefix"), 173 | }; 174 | 175 | return config; 176 | } 177 | } 178 | 179 | 180 | } 181 | -------------------------------------------------------------------------------- /WorkerHost/EventHubReader.cs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Corporation, Inc. All rights reserved. 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // --------------------------------------------------------------------------------- 24 | 25 | #define DEBUG_LOG 26 | 27 | using System; 28 | using System.Collections.Generic; 29 | using System.Diagnostics; 30 | using System.Linq; 31 | using System.Text; 32 | using System.Threading; 33 | using System.Threading.Tasks; 34 | using Microsoft.ServiceBus; 35 | using Microsoft.ServiceBus.Messaging; 36 | using Newtonsoft.Json; 37 | 38 | namespace WorkerHost 39 | { 40 | public class EventHubReader 41 | { 42 | private EventHubReceiver[] _receivers = null; 43 | private string _consumerGroupPrefix; 44 | 45 | private Task[] _tasks = null; 46 | private Action _onMessage; 47 | 48 | internal ManualResetEvent FailureEvent = new ManualResetEvent(false); 49 | 50 | public EventHubReader(string consumerGroupPrefix, Action onMessage) 51 | { 52 | _consumerGroupPrefix = consumerGroupPrefix; 53 | _onMessage = onMessage; 54 | } 55 | 56 | public void Close() 57 | { 58 | if (_receivers != null) 59 | { 60 | foreach (var r in _receivers) 61 | { 62 | r.CloseAsync(); 63 | } 64 | } 65 | } 66 | 67 | public void Run(string connectionString, string hubName, string measureNameFilter) 68 | { 69 | NamespaceManager nsmgr = NamespaceManager.CreateFromConnectionString(connectionString); 70 | EventHubDescription desc = nsmgr.GetEventHub(hubName); 71 | 72 | //we use already defined consumerGroup name to not reach limit on CG count 73 | string consumerGroupName = _consumerGroupPrefix; 74 | ConsumerGroupDescription consumerGroupDesc = nsmgr.CreateConsumerGroupIfNotExists(new ConsumerGroupDescription(hubName, consumerGroupName)); 75 | 76 | EventHubClient client = EventHubClient.CreateFromConnectionString(connectionString, hubName); 77 | 78 | int numPartitions = desc.PartitionCount; 79 | _receivers = new EventHubReceiver[numPartitions]; 80 | 81 | _tasks = new Task[numPartitions]; 82 | 83 | for (int iPart = 0; iPart < desc.PartitionCount; iPart++) 84 | { 85 | EventHubReceiver receiver = client.GetConsumerGroup(consumerGroupName).CreateReceiver( 86 | desc.PartitionIds[iPart], DateTime.UtcNow - TimeSpan.FromMinutes(2)); 87 | _receivers[iPart] = receiver; 88 | 89 | int part = iPart; 90 | Task.Factory.StartNew((state) => 91 | { 92 | try 93 | { 94 | while (true) 95 | { 96 | var messages = _receivers[part].Receive(1000, TimeSpan.FromSeconds(1)); 97 | Process(messages); 98 | } 99 | } 100 | catch (Exception ex) 101 | { 102 | //FailureEvent.Set(); 103 | Trace.TraceError("Ignored invalid event data: {0}"); 104 | } 105 | }, iPart); 106 | } 107 | } 108 | 109 | void Process(IEnumerable batch) 110 | { 111 | UTF8Encoding enc = new UTF8Encoding(); 112 | foreach (EventData e in batch) 113 | { 114 | string body = enc.GetString(e.GetBytes()); 115 | string[] lines = body.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); 116 | 117 | foreach (var line in lines) 118 | { 119 | try 120 | { 121 | _onMessage(line); 122 | } 123 | catch (Exception) 124 | { 125 | #if DEBUG_LOG 126 | Trace.TraceError("Ignored invalid event data: {0}", line); 127 | #endif 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /WorkerHost/Program.cs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Corporation, Inc. All rights reserved. 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // --------------------------------------------------------------------------------- 24 | //#define DEBUG_LOG 25 | using System; 26 | using System.Collections.Generic; 27 | using System.Configuration; 28 | using System.Diagnostics; 29 | using System.Linq; 30 | using System.Net; 31 | using System.Net.Mail; 32 | using System.Threading; 33 | using Microsoft.WindowsAzure.ServiceRuntime; 34 | using SendGrid; 35 | using Twilio; 36 | 37 | namespace WorkerHost 38 | { 39 | public class WorkerHost : RoleEntryPoint 40 | { 41 | private static EventHubReader _EventHubReader; 42 | private static Timer _Timer; 43 | 44 | private static object _SenderLock = new object(); 45 | private static Web _SendGridTransportWeb; 46 | private static SmtpClient _SmtpClient; 47 | private static TwilioRestClient _TwilioRestClient; 48 | 49 | private static MailAddress _FromAddress; 50 | private static MailAddress[] _ToAddress; 51 | 52 | private static AppConfiguration _Config; 53 | 54 | private static NotificationServiceType _NotificationService; 55 | enum NotificationServiceType 56 | { 57 | Smtp = 1, 58 | SendGridWeb = 2, 59 | Twilio = 3, 60 | TwilioCall = 4 61 | } 62 | 63 | static void Main() 64 | { 65 | StartHost("L0cal"); 66 | } 67 | 68 | public override void Run() 69 | { 70 | StartHost("R0le"); 71 | } 72 | 73 | private static void StartHost(string consumerGroupSuffix) 74 | { 75 | Trace.WriteLine("Starting Worker..."); 76 | #if DEBUG_LOG 77 | RoleEnvironment.TraceSource.TraceInformation("Starting Worker..."); 78 | #endif 79 | 80 | _Config = ConfigurationLoader.GetConfig(); 81 | if (_Config == null) 82 | { 83 | return; 84 | } 85 | _Config.ConsumerGroupPrefix += consumerGroupSuffix; 86 | 87 | _NotificationService = 88 | (NotificationServiceType)Enum.Parse(typeof(NotificationServiceType), _Config.NotificationService); 89 | 90 | var credentials = new NetworkCredential(_Config.EmailServiceUserName, _Config.EmailServicePassword); 91 | 92 | PrepareMailAddressInstances(); 93 | 94 | switch (_NotificationService) 95 | { 96 | case NotificationServiceType.Smtp: 97 | { 98 | _SmtpClient = new SmtpClient 99 | { 100 | Port = _Config.SmtpEnableSSL ? 587 : 25, 101 | DeliveryMethod = SmtpDeliveryMethod.Network, 102 | UseDefaultCredentials = false, 103 | EnableSsl = false, 104 | Host = _Config.SmtpHost, 105 | Credentials = credentials 106 | }; 107 | } 108 | break; 109 | case NotificationServiceType.SendGridWeb: 110 | { 111 | _SendGridTransportWeb = new Web(credentials); 112 | } 113 | break; 114 | case NotificationServiceType.Twilio: 115 | case NotificationServiceType.TwilioCall: 116 | { 117 | string ACCOUNT_SID = _Config.EmailServiceUserName; 118 | string AUTH_TOKEN = _Config.EmailServicePassword; 119 | 120 | _TwilioRestClient = new TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN); 121 | } 122 | break; 123 | } 124 | 125 | _EventHubReader = new EventHubReader(_Config.ConsumerGroupPrefix, OnMessage); 126 | 127 | Process(); 128 | } 129 | 130 | private static void PrepareMailAddressInstances() 131 | { 132 | try 133 | { 134 | _FromAddress = new MailAddress(_Config.MessageFromAddress, _Config.MessageFromName); 135 | } 136 | catch (Exception) 137 | { 138 | //incorrect mail address in config, maybe a phone number 139 | } 140 | 141 | IList mailAddressList = new List(); 142 | 143 | foreach (var sendTo in _Config.SendToList) 144 | { 145 | try 146 | { 147 | mailAddressList.Add(new MailAddress(sendTo)); 148 | } 149 | catch (Exception) 150 | { 151 | //incorrect mail address in config, maybe a phone number 152 | } 153 | } 154 | 155 | _ToAddress = mailAddressList.ToArray(); 156 | } 157 | 158 | public static void Process() 159 | { 160 | _EventHubReader.Run(_Config.DeviceSBConnectionString, _Config.DeviceEHName, string.Empty); 161 | _EventHubReader.FailureEvent.WaitOne(); 162 | } 163 | 164 | private static void OnMessage(string serializedData) 165 | { 166 | lock (_SenderLock) 167 | { 168 | try 169 | { 170 | if (!_Config.SendToList.Any()) return; 171 | 172 | string messageBody = "Message Received: \n" + serializedData; 173 | 174 | if (_SmtpClient != null) 175 | { 176 | foreach (var mailTo in _ToAddress) 177 | { 178 | try 179 | { 180 | MailMessage myMessage = new MailMessage(_FromAddress, mailTo) 181 | { 182 | Subject = _Config.MessageSubject, 183 | Body = messageBody 184 | }; 185 | 186 | _SmtpClient.Send(myMessage); 187 | } 188 | catch (Exception) 189 | { 190 | } 191 | } 192 | } 193 | 194 | if (_SendGridTransportWeb != null) 195 | { 196 | SendGridMessage myMessage = new SendGridMessage( 197 | _FromAddress, 198 | _ToAddress, 199 | _Config.MessageSubject, 200 | string.Empty, 201 | messageBody 202 | ); 203 | 204 | _SendGridTransportWeb.DeliverAsync(myMessage).Wait(); 205 | } 206 | 207 | if (_TwilioRestClient != null) 208 | { 209 | 210 | switch (_NotificationService) 211 | { 212 | case NotificationServiceType.Twilio: 213 | { 214 | foreach (var smsTo in _Config.SendToList) 215 | { 216 | try 217 | { 218 | _TwilioRestClient.SendMessage(_Config.MessageFromAddress, smsTo, 219 | messageBody); 220 | } 221 | catch (Exception) 222 | { 223 | } 224 | } 225 | } 226 | break; 227 | case NotificationServiceType.TwilioCall: 228 | { 229 | string callUrl = "http://twimlets.com/message?Message%5B0%5D=" + WebUtility.UrlEncode(_Config.MessageSubject); 230 | 231 | foreach (var callTo in _Config.SendToList) 232 | { 233 | try 234 | { 235 | CallOptions options = new CallOptions 236 | { 237 | From = _Config.MessageFromAddress, 238 | To = callTo, 239 | Url = callUrl 240 | }; 241 | 242 | Call call = _TwilioRestClient.InitiateOutboundCall(options); 243 | } 244 | catch (Exception) 245 | { 246 | } 247 | } 248 | } 249 | break; 250 | } 251 | } 252 | } 253 | catch (Exception) 254 | { 255 | Trace.WriteLine("Exception on mail sending..."); 256 | } 257 | } 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /WorkerHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Corporation, Inc. All rights reserved. 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // --------------------------------------------------------------------------------- 24 | 25 | using System.Reflection; 26 | using System.Runtime.CompilerServices; 27 | using System.Runtime.InteropServices; 28 | 29 | // General Information about an assembly is controlled through the following 30 | // set of attributes. Change these attribute values to modify the information 31 | // associated with an assembly. 32 | [assembly: AssemblyTitle("WorkerHost")] 33 | [assembly: AssemblyDescription("")] 34 | [assembly: AssemblyConfiguration("")] 35 | [assembly: AssemblyCompany("")] 36 | [assembly: AssemblyProduct("WorkerHost")] 37 | [assembly: AssemblyCopyright("Copyright © 2014")] 38 | [assembly: AssemblyTrademark("")] 39 | [assembly: AssemblyCulture("")] 40 | 41 | // Setting ComVisible to false makes the types in this assembly not visible 42 | // to COM components. If you need to access a type in this assembly from 43 | // COM, set the ComVisible attribute to true on that type. 44 | [assembly: ComVisible(false)] 45 | 46 | // The following GUID is for the ID of the typelib if this project is exposed to COM 47 | [assembly: Guid("76fab21f-d875-451d-abf7-e8c352fc7a9f")] 48 | 49 | // Version information for an assembly consists of the following four values: 50 | // 51 | // Major Version 52 | // Minor Version 53 | // Build Number 54 | // Revision 55 | // 56 | // You can specify all the values or you can default the Build and Revision Numbers 57 | // by using the '*' as shown below: 58 | // [assembly: AssemblyVersion("1.0.*")] 59 | [assembly: AssemblyVersion("1.0.0.0")] 60 | [assembly: AssemblyFileVersion("1.0.0.0")] 61 | -------------------------------------------------------------------------------- /WorkerHost/WorkerHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {25079398-1602-45EE-837C-D4195A1FBC27} 8 | Exe 9 | Properties 10 | WorkerHost 11 | WorkerHost 12 | v4.5 13 | 512 14 | false 15 | Worker 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 1 27 | 1.0.0.%2a 28 | false 29 | true 30 | true 31 | 32 | 33 | AnyCPU 34 | true 35 | full 36 | false 37 | bin\Debug\ 38 | DEBUG;TRACE 39 | prompt 40 | 4 41 | 42 | 43 | AnyCPU 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | 51 | 52 | 95CCA3EE94BEF7B07D71337320FD2BBC359DC7C6 53 | 54 | 55 | WorkerHost_TemporaryKey.pfx 56 | 57 | 58 | true 59 | 60 | 61 | false 62 | 63 | 64 | 65 | ..\packages\Hyak.Common.1.0.2\lib\net45\Hyak.Common.dll 66 | 67 | 68 | ..\packages\Microsoft.Azure.Common.2.0.4\lib\net45\Microsoft.Azure.Common.dll 69 | 70 | 71 | ..\packages\Microsoft.Azure.Common.2.0.4\lib\net45\Microsoft.Azure.Common.NetFramework.dll 72 | 73 | 74 | ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll 75 | True 76 | 77 | 78 | ..\packages\Microsoft.Data.Edm.5.6.4\lib\net40\Microsoft.Data.Edm.dll 79 | True 80 | 81 | 82 | ..\packages\Microsoft.Data.OData.5.6.4\lib\net40\Microsoft.Data.OData.dll 83 | True 84 | 85 | 86 | ..\packages\Microsoft.Data.Services.Client.5.6.4\lib\net40\Microsoft.Data.Services.Client.dll 87 | True 88 | 89 | 90 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.1.203031538-alpha\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll 91 | 92 | 93 | ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.1.203031538-alpha\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll 94 | 95 | 96 | False 97 | ..\packages\WindowsAzure.ServiceBus.2.6.4\lib\net40-full\Microsoft.ServiceBus.dll 98 | 99 | 100 | ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll 101 | 102 | 103 | ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll 104 | 105 | 106 | ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll 107 | 108 | 109 | ..\packages\Microsoft.WindowsAzure.Common.1.4.1\lib\net45\Microsoft.WindowsAzure.Common.dll 110 | 111 | 112 | ..\packages\Microsoft.WindowsAzure.Common.1.4.1\lib\net45\Microsoft.WindowsAzure.Common.NetFramework.dll 113 | 114 | 115 | False 116 | ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll 117 | 118 | 119 | True 120 | 121 | 122 | ..\packages\Microsoft.WindowsAzure.Management.ServiceBus.0.17.1-preview\lib\net40\Microsoft.WindowsAzure.Management.ServiceBus.dll 123 | 124 | 125 | False 126 | 127 | 128 | ..\packages\WindowsAzure.Storage.5.0.2\lib\net40\Microsoft.WindowsAzure.Storage.dll 129 | True 130 | 131 | 132 | False 133 | ..\packages\Newtonsoft.Json.7.0.1-beta1\lib\net45\Newtonsoft.Json.dll 134 | 135 | 136 | ..\packages\RestSharp.105.2.2\lib\net45\RestSharp.dll 137 | True 138 | 139 | 140 | ..\packages\SendGrid.SmtpApi.1.3.1\lib\net40\SendGrid.SmtpApi.dll 141 | True 142 | 143 | 144 | ..\packages\Sendgrid.6.1.0\lib\SendGridMail.dll 145 | True 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll 157 | 158 | 159 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll 160 | 161 | 162 | 163 | 164 | 165 | ..\packages\System.Spatial.5.6.4\lib\net40\System.Spatial.dll 166 | True 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | ..\packages\Twilio.4.4.0\lib\3.5\Twilio.Api.dll 175 | True 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | Designer 187 | 188 | 189 | 190 | 191 | 192 | False 193 | Microsoft .NET Framework 4.5 %28x86 and x64%29 194 | true 195 | 196 | 197 | False 198 | .NET Framework 3.5 SP1 Client Profile 199 | false 200 | 201 | 202 | False 203 | .NET Framework 3.5 SP1 204 | false 205 | 206 | 207 | 208 | 215 | -------------------------------------------------------------------------------- /WorkerHost/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /WorkerRole/ServiceConfiguration.Cloud.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /WorkerRole/ServiceConfiguration.Local.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /WorkerRole/ServiceDefinition.csdef: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /WorkerRole/WorkerHostContent/diagnostics.wadcfgx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | -------------------------------------------------------------------------------- /WorkerRole/WorkerRole.ccproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.7 8 | 73173d11-aa67-4089-91d5-bbe0bd4c5444 9 | Library 10 | Properties 11 | WorkerRole 12 | WorkerRole 13 | True 14 | WorkerRole 15 | False 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | WorkerHost 43 | {25079398-1602-45ee-837c-d4195a1fbc27} 44 | True 45 | Worker 46 | WorkerHost 47 | True 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 10.0 60 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Windows Azure Tools\2.7\ 61 | 62 | 63 | -------------------------------------------------------------------------------- /contribute.md: -------------------------------------------------------------------------------- 1 | # Contributing to Azure samples # 2 | 3 | Thank you for your interest in contributing to Azure samples! 4 | 5 | ## Ways to contribute 6 | 7 | You can contribute to [Azure samples](https://azure.microsoft.com/documentation/samples/) in a few different ways: 8 | 9 | - Submit feedback on [this sample page](https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications) whether it was helpful or not. 10 | - Submit issues through [issue tracker](https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications/issues) on GitHub. We are actively monitoring the issues and improving our samples. 11 | - If you wish to make code changes to samples, or contribute something new, please follow the [GitHub Forks / Pull requests model](https://help.github.com/articles/fork-a-repo/): Fork the sample repo, make the change and propose it back by submitting a pull request. 12 | -------------------------------------------------------------------------------- /event-hubs-erp-alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/event-hubs-dotnet-user-notifications/e132a8f6b1139c0df5168bf04311ff04e14bf356/event-hubs-erp-alert.png -------------------------------------------------------------------------------- /event-hubs-sensor-alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/event-hubs-dotnet-user-notifications/e132a8f6b1139c0df5168bf04311ff04e14bf356/event-hubs-sensor-alert.png -------------------------------------------------------------------------------- /event-hubs-sensors-notify-users.md: -------------------------------------------------------------------------------- 1 | # Notify users of data received from sensors or other systems 2 | Suppose you have an application that monitors data in real time, or produces reports on a schedule. If you look at the website on which those real-time charts or reports are displayed, you might see something that requires action. What if you need to be alerted to those situations, rather than relying on remembering to check the website? Imagine that you have a grow light in a greenhouse, and you need to know immediately if the light goes out. One way to do that would be with a light sensor in the greenhouse, arranging to be sent an email if the light is off. 3 | 4 | ![][1] 5 | 6 | In another scenario, imagine that you run a pet boarding facility, and you must be alerted to low inventory supply levels. For example, you might arrange to be sent a text message from your ERP system if your warehouse inventory of dog food has fallen to a critical level. 7 | 8 | ![][2] 9 | 10 | The problem is how to get critical information when certain conditions are met, not when you get around to checking out a static report. If you are using an [Azure Event Hub][Azure Event Hub] or [Azure IoT Hub][Azure IoT Hub] to receive data from devices or enterprise applications such as [Dynamics AX][Dynamics AX], you have several options for how to process them. You can view them on a website, you can analyze them, you can store them, and you can use them to trigger commands to do something. To do this, you can use powerful tools such as [Azure Websites][Azure Websites], [SQL Azure][SQL Azure], [HDInsight][HDInsight], [Cortana Intelligence Suite][Cortana Intelligence Suite], [IoT Suite][IoT Suite], [Logic Apps][Logic Apps], or [Azure Notification Hubs][Azure Notification Hubs]. But sometimes all you want to do is to send that data to someone with a minimum of overhead. To show you how to do that with just a little bit of code, we’ve provided a new sample, [AppToNotifyUsers][AppToNotifyUsers]. Options included are email (SMTP), SMS, and phone. 11 | 12 | ## Application structure 13 | The application is written in C#, and the readme file in the sample contains all the info you need to modify, build, and publish the application. The following sections provide a high-level overview of what the application does. 14 | 15 | We start with the assumption that you have critical events being pushed to an Azure Event Hub or IoT Hub. Any hub will do, as long as you have access to it and know the connection string. 16 | 17 | If you do not already have an Event Hub or IoT hub, you can easily set up a test bed with an Arduino shield and a Raspberry Pi, following the instructions in the [Connect The Dots](https://github.com/Azure/connectthedots) project. The light sensor on the Arduino shield sends the light levels through the Pi to an [Azure Event Hub][Azure Event Hub] (**ehdevices**), and an [Azure Stream Analytics](https://azure.microsoft.com/services/stream-analytics/) job pushes alerts to a second event hub (**ehalerts**) if the light levels received fall below a certain level. 18 | 19 | When **AppToNotify** starts, it reads a configuration file (App.config) to get the URL and credentials for the Event Hub receiving the alerts. It then spawns a process to continuously monitor that Event Hub for any message that comes through – as long as you have can access the URL for the Event Hub or IoT hub and valid credentials, this Event Hubs reader code will continuously read what's coming in. During startup, the application also reads the URL and credentials for the messaging service (email, SMS, phone) you want to use, and the name/address of the sender and a list of recipients. 20 | 21 | Once the Event Hub monitor detects a message, it triggers a process that sends that message using the method specified in the configuration file. Note that it sends every message it detects. If you set the monitor to point to an Event Hub that receives ten messages per second, the sender will send ten messages per second – ten emails per second, ten SMS messages per second, ten phone calls per second. For that reason, make sure that you monitor an Event Hub that only receives the alerts that need to be sent out, not an Event Hub that receives all the raw data from your sensors or applications. 22 | 23 | ## Applicability 24 | The code in this sample only shows how to monitor Event Hubs and how to call external messaging services in the event that you want to add this functionality to your application. Note that this solution is a DIY, developer-focused example only. It does not address enterprise requirements such as redundancy, fail-over, restart upon failure, etc. For more comprehensive and production solutions, see the following: 25 | 26 | * Using connectors or push notifications using the [Azure Logic Apps](../app-service-logic/app-service-logic-connectors-list.md) service. 27 | * Using [Azure Notification Hubs](https://msdn.microsoft.com/library/azure/jj927170.aspx), as described the blog [Broadcast push notifications to millions of mobile devices using Azure Notification Hubs](http://weblogs.asp.net/scottgu/broadcast-push-notifications-to-millions-of-mobile-devices-using-windows-azure-notification-hubs). 28 | 29 | ## Next steps 30 | It is straightforward to create a simple notification service that sends emails or text messages to recipients, or calls them, to relay data received by an Event Hub or IoT Hub. To deploy the solution to notify users based upon data received by these hubs, visit [AppToNotifyUsers][AppToNotifyUsers]. 31 | 32 | For more information about these hubs, see the following articles: 33 | 34 | * [Azure Event Hubs] 35 | * [Azure IoT Hub] 36 | * Get started with an [Event Hubs tutorial]. 37 | * A complete [sample application that uses Event Hubs]. 38 | 39 | To deploy the solution to notify users based on data received by these hubs, visit: 40 | 41 | * [AppToNotifyUsers][AppToNotifyUsers] 42 | 43 | [Event Hubs tutorial]: event-hubs-csharp-ephcs-getstarted.md 44 | [Azure IoT Hub]: https://azure.microsoft.com/services/iot-hub/ 45 | [Azure Event Hubs]: https://azure.microsoft.com/services/event-hubs/ 46 | [Azure Event Hub]: https://azure.microsoft.com/services/event-hubs/ 47 | [sample application that uses Event Hubs]: https://code.msdn.microsoft.com/Service-Bus-Event-Hub-286fd097 48 | [AppToNotifyUsers]: https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications 49 | [Dynamics AX]: http://www.microsoft.com/dynamics/erp-ax-overview.aspx 50 | [Azure Websites]: https://azure.microsoft.com/services/app-service/web/ 51 | [SQL Azure]: https://azure.microsoft.com/services/sql-database/ 52 | [HDInsight]: https://azure.microsoft.com/services/hdinsight/ 53 | [Cortana Intelligence Suite]: http://www.microsoft.com/server-cloud/cortana-analytics-suite/Overview.aspx?WT.srch=1&WT.mc_ID=SEM_lLFwOJm3&bknode=BlueKai 54 | [IoT Suite]: https://azure.microsoft.com/solutions/iot-suite/ 55 | [Logic Apps]: https://azure.microsoft.com/services/app-service/logic/ 56 | [Azure Notification Hubs]: https://azure.microsoft.com/services/notification-hubs/ 57 | [Azure Stream Analytics]: https://azure.microsoft.com/services/stream-analytics/ 58 | 59 | [1]: event-hubs-sensor-alert.png 60 | [2]: event-hubs-erp-alert.png 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | services: event-hubs, iot-hub, cloud-services, notification-hubs 3 | platforms: dotnet 4 | author: spyrossak 5 | --- 6 | 7 | # Notify users of events received by an event or IoT hub 8 | 9 | For more information about this sample, see the [Notify users of data received from sensors or other systems](https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications/blob/master/event-hubs-sensors-notify-users.md) topic in this repository. 10 | 11 | If you are using Azure Stream Analytics or Azure Machine Learning to generate alerts based upon data coming from your devices, you have various options on how to display those alerts. For example, as in the [Connect The Dots](https://github.com/Azure/connectthedots) project, you could display them on a website, so users could see them in real-time if they are looking at a web page. By contrast, the code here, in the [AppToNotifyUsers](https://github.com/Azure-Samples/event-hubs-dotnet-user-notifications) solution, provides a very basic and stand-alone application for selected users to be notified of alerts. It does so by creating an Azure Cloud Service (worker role) that monitors the assigned event hub and pushes that data to a notification service specified by the administrator. Notification options in the solution include: 12 | 13 | - SMTP 14 | - SMS 15 | - Phone 16 | 17 | The user can easily add alternative notification options (such as Twitter), following the work flow in the current solution. Note that each of these solutions require a subscription to an external service (e.g. an email service if notifying users over email). A different architecture, using Twitter for Notifications is shown in Olivier Bloch's posting [Tweet vibration anomalies detected by Azure IoT services on data from an Intel Edison running Node.js](https://azure.microsoft.com/en-us/documentation/samples/iot-hub-nodejs-intel-edison-vibration-anomaly-detection/). 18 | 19 | Note that this solution is a DIY, developer-focused example only. It does not address enterprise requirements such as redundancy, fail-over, restart upon failure, etc. For more comprehensive and production solutions, check out the following: 20 | 21 | * Using connectors or push notifications from an Azure Notification Hub available in [Logic Apps](https://azure.microsoft.com/en-us/documentation/articles/app-service-logic-connectors-list) 22 | * See this [Notification Hubs Overview](https://msdn.microsoft.com/library/azure/jj927170.aspx) for background on Notification Hubs 23 | * [Broadcast push notifications to millions of mobile devices using Windows Azure Notification Hubs](http://weblogs.asp.net/scottgu/broadcast-push-notifications-to-millions-of-mobile-devices-using-windows-azure-notification-hubs) by Scott Guthrie 24 | 25 | 26 | ## WARNING ## 27 | 28 | This application runs in the cloud, and will push ALL the data your event hub of choice receives to the users you list. The anticipated scenario is that you monitor an event hub that is dedicated to receiving alerts on a sporadic basis (maybe once a day or once a week), in which case your targeted users will get an alert pushed to them once a day or once a week. If, however, you monitor an event hub that is getting data every second then your users will get an alert once a second. Realize that it may take a few minutes to stop a cloud service once it is running, so that your user(s) may get 60 emails a minute until the service is fully shut down if you make the wrong choice - assuming you are at a computer and able to connect to the Azure management portal to stop the service! We strongly suggest you do not set this up and then go away for a two week vacation without testing anticipated scenarios... 29 | 30 | 31 | # Prerequisites # 32 | 33 | In order to configure and deploy the AppToNotifyUsers application you will need to have set up an Event Hub and know the Connection String. The easiest way to do this is to use [AzurePrep](https://github.com/Azure/connectthedots/tree/master/Azure/AzurePrep ) in the Connect The Dots repo, but that is not a prerequisite - set up the Event Hub manually if you like. 34 | 35 | You will also need an account or subscription to the user service of your choice - for example an email service such as your ISP, or a computer-to-SMS or Voice service. 36 | 37 | # Setup Tasks # 38 | 39 | Setting up the application once you have an Event Hub and its Connection String involves the following tasks, which will be described in greater detail below. 40 | 41 | 1. Get and set up a subscription for the notification service of your choice 42 | 2. Clone or copy the project to your machine 43 | 2. Open AppToNotifyUsers.sln solution in Visual Studio 44 | 3. Edit App.config in the Worker Host folder to provide the following 45 | 1. The connection string to your Event Hub 46 | 2. The URL for the web service which you will use to push the data 47 | 3. The credentials you will use for accessing that web service 48 | 4. The information about the sender 49 | 5. The information about the users to be notified 50 | 4. Build the project from the *BUILD* menu (Select Release, not Debug) 51 | 5. Publish the application to your Azure subscription 52 | 6. Generate an alert by doing something to one of your devices, and verify that the alert is making it to your event hub (ehalerts) 53 | 7. Verify that the alert is making it to the targeted user (email, SMS, or phone call received by the listed users). 54 | 55 | 56 | # Editing app.config # 57 | 58 | There are three sections of App.config you will need to change - to specify the Event Hub to monitor, to specify the method by which the messages will be sent, and to specify the sender and recipients of the messages. 59 | 60 | ## Step 1: Specifying the Event Hub to Monitor ## 61 | The code in AppToNotifyUsers creates an Azure Cloud Service (worker role) that monitors an event hub identified by a URL you list a config file, App.config, together with the Shared Key that grants you access. The strings in App.config that needs to be modified are the following: 62 | ``` 63 | 64 | 65 | ``` 66 | If you deploy the example in the Connect The Dots, that event hub is called ehalerts, and you would replace [Event Hub name] with 'ehalerts', and the ServiceBusConnectionString string with the connection string for it, that you can find in the Azure management portal. It should look something like this: 67 | 68 | ``` 69 | 70 | 71 | ``` 72 | The EventHubReader module in the code uses this information to get messages from ehalerts, and put it in a queue to be sent by whatever method you specify. 73 | 74 | ## Step 2: Select the outbound messaging service ## 75 | Notification options in the solution are encoded as separate subroutines that are called depending upon entries in the App.Config file. Currently there are three options included in the sample code: 76 | 77 | * SMTP 78 | * SMS 79 | * Phone 80 | 81 | As with the Event Hub, you need to specify in App.Config the service you will be using to push the alerts and the credentials for that service. The keys are as follows: 82 | ``` 83 | 84 | 85 | 86 | 87 | 88 | ``` 89 | If you want to use email to send your alerts, replace [Service option] with 'SMTP', the SMTPHost with your email server name, and enter the credentials that are allowed to use that service. If you want to use SMS, and have a subscription to a service such as Twilio, replace [Service option] with 'SMS', and so forth. You can add additional or alternative notification options easily, such as using Twitter, following the work flow in the current solution. Note: Each of these solutions require a subscription to an external service (e.g. an email service if notifying users over email). 90 | 91 | To repeat what was said earlier, this solution is strictly a DIY, developer-focused example only. For more comprehensive production-level solutions, please check out the other solutions listed at the beginning of this file. 92 | ## Step 3: Identify the Sender and Recipients of the Messages ## 93 | Once you have specified how messages will be sent, you need to identify from whom, and to whom, they will be sent. If the Notification Service is SMTP, either using an SMTP host to which you have access, or using SendGrid, you would specify an email address in the sendFrom address in App.Config: 94 | ``` 95 | 96 | ``` 97 | If you are using an SMTP (email) sender, this would be the display name and email alias shown on the From and Subject lines of the email. Note that the sender address you enter will be that shown on the email as received by the recipient. If they are expected to reply, that should be a real email recipient. Furthermore, if you are sending a lot of email to various recipients, make sure that they are ok with getting those emails, so your sender does not get labeled as a spammer! 98 | 99 | If you are using a service like Twilio to send SMS messages, this would be the phone number Twilio assigns to you for the sender of the SMS. Similarly for the recipient list: 100 | ``` 101 | 102 | 103 | 104 | 105 | ``` 106 | 107 | 108 | 109 | # Publishing the application # 110 | You use Visual Studio to publish and start the application. The steps are as follows: 111 | * In Visual Studio, right-click on 'WorkerRole' in Solution 'AppToNotifyUsers', and select *Publish*. 112 | * In the Publish Azure Application, answer the following questions. 113 | * Name: [pick something unique] 114 | * Region: [pick same region as you used for the Event Hub] 115 | * Database server: no database 116 | * Database password: [leave suggested password] 117 | * Click Publish, and wait until the status bar shows "Completed". At that point the application is running in your subscription, polling the event hub you listed, and pushing everything it receives to the users you listed over the service you picked. 118 | --------------------------------------------------------------------------------