Skip to content
This repository
Browse code

win app: ifttt first light, slight refactoring (moving input methods …

…into Blink1Input), small changes to Blink1Lib API
  • Loading branch information...
commit 13b8f26cd8982e57134be8e8043acd31c5cf0756 1 parent f7165a6
Tod E. Kurt authored January 05, 2013
3  windows/Blink1Control/Blink1Control/App.config
@@ -13,9 +13,6 @@
13 13
             <setting name="hostId" serializeAs="String">
14 14
                 <value>00000000</value>
15 15
             </setting>
16  
-            <setting name="todtest" serializeAs="String">
17  
-                <value>hello</value>
18  
-            </setting>
19 16
         </Blink1Control.Properties.Settings>
20 17
     </userSettings>
21 18
 </configuration>
2  windows/Blink1Control/Blink1Control/Blink1Control.csproj
@@ -55,8 +55,8 @@
55 55
       <Generator>MSBuild:Compile</Generator>
56 56
       <SubType>Designer</SubType>
57 57
     </ApplicationDefinition>
  58
+    <Compile Include="Blink1Event.cs" />
58 59
     <Compile Include="MySettings.cs" />
59  
-    <Compile Include="Settings.cs" />
60 60
     <Page Include="MainWindow.xaml">
61 61
       <Generator>MSBuild:Compile</Generator>
62 62
       <SubType>Designer</SubType>
24  windows/Blink1Control/Blink1Control/Blink1Event.cs
... ...
@@ -0,0 +1,24 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+using System.Threading.Tasks;
  6
+
  7
+namespace Blink1Control
  8
+{
  9
+    // thanks to http://json2csharp.com/ for this
  10
+    public class IftttEvent
  11
+    {
  12
+        public string blink1_id { get; set; }
  13
+        public string name { get; set; }
  14
+        public string source { get; set; }
  15
+        public string date { get; set; }
  16
+    }
  17
+
  18
+    public class IftttResponse
  19
+    {
  20
+        public List<IftttEvent> events { get; set; }
  21
+        public int event_count { get; set; }
  22
+        public string status { get; set; }
  23
+    }
  24
+}
223  windows/Blink1Control/Blink1Control/Blink1Input.cs
@@ -4,12 +4,18 @@
4 4
 using System.Text;
5 5
 using System.Threading.Tasks;
6 6
 //using System.Runtime.Serialization;
  7
+using System.Text.RegularExpressions;
  8
+using System.Net;
  9
+using System.IO;
  10
+using Newtonsoft.Json;
7 11
 
8 12
 namespace Blink1Control
9 13
 {
10 14
 //    [Serializable]
11 15
     public class Blink1Input
12 16
     {
  17
+
  18
+        public Blink1Server blink1Server { private get; set; }
13 19
         /// <summary>
14 20
         /// Name of the input 
15 21
         /// </summary>
@@ -26,21 +32,236 @@ public class Blink1Input
26 32
         public string arg2 { get; set; }
27 33
         public string arg3 { get; set; }
28 34
         public string lastVal { get; set; }
  35
+        public string possibleVals { get; set; }
  36
+
  37
+        // each input can have an independent update interval that's greater than master interval
  38
+        public DateTime lastTime;
  39
+        private int updateInterval;
  40
+        // holder of last valid response/content
  41
+        private string lastContent;
29 42
 
  43
+        // FIXME: hacks
  44
+        public static DateTime iftttLastTime;
  45
+        public static int iftttUpdateInterval;
  46
+        public static string iftttLastContent;
  47
+
  48
+        /// <summary>
  49
+        /// Constructor
  50
+        /// </summary>
  51
+        /// <param name="name"></param>
  52
+        /// <param name="type"></param>
  53
+        /// <param name="arg1"></param>
  54
+        /// <param name="arg2"></param>
  55
+        /// <param name="arg3"></param>
30 56
         public Blink1Input(string name, string type, string arg1, string arg2, string arg3)
31 57
         {
32 58
             this.iname = name; this.type = type;
33 59
             this.arg1 = arg1; this.arg2 = arg2; this.arg3 = arg3;
34 60
         }
35 61
 
  62
+        /// <summary>
  63
+        /// 
  64
+        /// </summary>
  65
+        public void stop()
  66
+        {
  67
+        }
36 68
 
  69
+        /// <summary>
  70
+        /// Called periodically by blink1Server
  71
+        /// </summary>
37 72
         public void update()
38 73
         {
  74
+            if (type.Equals("ifttt")) { // FIXME: there's totally a better way of doing this
  75
+                updateIftttInput();
  76
+            }
  77
+            else if (type.Equals("url")) {
  78
+                updateUrlInput();
  79
+            }
  80
+            else if (type.Equals("file")) {
  81
+                updateFileInput();
  82
+            }
  83
+            else if (type.Equals("script")) {
  84
+                updateScriptInput();
  85
+            }
  86
+
39 87
         }
40 88
 
41  
-        public void stop()
  89
+        // periodically fetch events from IFTTT gateway
  90
+        public void updateIftttInput()
  91
+        {
  92
+            if (iftttLastContent==null) return;
  93
+            
  94
+            // add interval checking
  95
+
  96
+            string rulename = arg1;
  97
+
  98
+            IftttResponse iftttResponse = JsonConvert.DeserializeObject<IftttResponse>( iftttLastContent );
  99
+            if (iftttResponse.event_count > 0) {
  100
+                foreach (IftttEvent ev in iftttResponse.events) {
  101
+                    possibleVals += ev.name;
  102
+                    lastVal = ev.source;
  103
+                }
  104
+            }
  105
+        }
  106
+
  107
+        /// <summary>
  108
+        /// Periodically fetch URL, looking for color patern name or color code 
  109
+        /// </summary>
  110
+        public void updateUrlInput()
  111
+        {
  112
+            string url = arg1;
  113
+            string resp = getContentsOfUrl(url);
  114
+            lastContent = resp;
  115
+            if (resp == null) {
  116
+                lastVal = "bad url";
  117
+                return;
  118
+            }
  119
+            string patternstr = parsePatternOrColorString(resp);
  120
+
  121
+            if (patternstr != null && !patternstr.Equals(lastVal)) {
  122
+                lastVal = patternstr;
  123
+                blink1Server.playPattern(patternstr);
  124
+            }
  125
+        }
  126
+
  127
+        /// <summary>
  128
+        /// Periodically check file, looking for color pattern name or color code
  129
+        ///
  130
+        /// </summary>
  131
+        public void updateFileInput()
  132
+        {
  133
+            string filepath = arg1;
  134
+            string resp = getContentsOfFile(filepath);
  135
+            lastContent = resp;
  136
+            if (resp == null) {
  137
+                lastVal = "bad filename";
  138
+                return;
  139
+            }
  140
+            string patternstr = parsePatternOrColorString(resp);
  141
+            if (patternstr != null && !patternstr.Equals(lastVal)) {
  142
+                lastVal = patternstr;
  143
+                blink1Server.playPattern(patternstr);
  144
+            }
  145
+        }
  146
+
  147
+        /// <summary>
  148
+        /// Periodically excecute script, looking for color pattern name or color cod
  149
+        /// </summary>
  150
+        public void updateScriptInput()
  151
+        {
  152
+            string scriptname = arg1;
  153
+        }
  154
+
  155
+        // ---------------------------------------------------------------------------------------
  156
+        // static utility methods 
  157
+
  158
+
  159
+        /// <summary>
  160
+        /// This is called peridically, but only once for all IFTTT inputs. 
  161
+        /// FIXE: This whole method is a super hack
  162
+        /// </summary>
  163
+        /// <param name="input"></param>
  164
+        /// <param name="normalmode"></param>
  165
+        public static void getIftttResponse(Boolean normalmode)
42 166
         {
  167
+            // only update URLs every 30 secs
  168
+            DateTime now = DateTime.Now; ; //[[NSDate date] timeIntervalSince1970];
  169
+            TimeSpan diff = now - iftttLastTime;            
  170
+            if( normalmode && (diff.Seconds < iftttUpdateInterval) ) { 
  171
+                return;
  172
+            }
  173
+            iftttLastTime = now;
  174
+
  175
+            string eventUrl = Blink1Server.iftttEventUrl +"/"+ Blink1Server.blink1Id;  // FIXME: hack
  176
+            iftttLastContent = getContentsOfUrl(eventUrl);
  177
+        }
  178
+
  179
+        /// <summary>
  180
+        /// Look for a color pattern designator or a HTML color hex code in a string
  181
+        /// </summary>
  182
+        /// <param name="str"></param>
  183
+        /// <returns></returns>
  184
+        public static string parsePatternOrColorString(string str)
  185
+        {
  186
+            // read color pattern, can be of form: pattern: "my pattern"
  187
+            Regex patternregex = new Regex(@"\""*pattern\""*:\s*\""(.+?)\""");
  188
+            Match matchp = patternregex.Match(str);
  189
+            if (matchp.Success) {
  190
+                return matchp.Groups[1].Value;
  191
+            }
  192
+            //
  193
+            Regex hexcoderegex = new Regex(@"(#[A-Fa-f0-9]{6})");
  194
+            Match matchc = hexcoderegex.Match(str.ToUpper());
  195
+            if (matchc.Success) {
  196
+                return matchc.Groups[1].Value;
  197
+            }
  198
+
  199
+            //Color c = ColorTranslator.FromHtml(str);
  200
+            return null;
43 201
         }
44 202
 
  203
+        /// <summary>
  204
+        /// Get the contents of a URL into string
  205
+        /// </summary>
  206
+        /// <param name="url">the url to fetch</param>
  207
+        /// <returns>contents of url or null</returns>
  208
+        public static string getContentsOfUrl(string url)
  209
+        {
  210
+            // stolen from: http://stackoverflow.com/questions/9961220/webclient-read-content-of-error-page
  211
+            Console.WriteLine("getContentsOfUrl:" + url);  // FIXME: how to do debug logging better?
  212
+            string content = null;
  213
+            WebClient webclient = new WebClient();
  214
+            try {
  215
+                content = webclient.DownloadString(url);
  216
+            }
  217
+            catch (WebException we) {
  218
+                // WebException.Status holds useful information
  219
+                Console.WriteLine(we.Message + "\n" + we.Status.ToString());  // FIXME:
  220
+                Stream receiveStream = we.Response.GetResponseStream();
  221
+                Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
  222
+                StreamReader readStream = new StreamReader(receiveStream, encode);
  223
+                content = readStream.ReadToEnd();
  224
+            }
  225
+            catch (NotSupportedException ne) {
  226
+                Console.WriteLine(ne.Message);   // other errors
  227
+            }
  228
+            return content;
  229
+        }
  230
+        /// <summary>
  231
+        /// Get contents of file into string
  232
+        /// </summary>
  233
+        /// <param name="filepath">full filepath of file to read</param>
  234
+        /// <returns>contents of file or null</returns>
  235
+        public static string getContentsOfFile(string filepath)
  236
+        {
  237
+            try {
  238
+                using (StreamReader sr = new StreamReader(filepath)) {
  239
+                    String contents = sr.ReadToEnd();
  240
+                    Console.WriteLine("file contents:" + contents);
  241
+                    return contents;
  242
+                }
  243
+            }
  244
+            catch (Exception e) {
  245
+                Console.WriteLine("The file could not be read:");
  246
+                Console.WriteLine(e.Message);
  247
+            }
  248
+            return null;
  249
+        }
  250
+
  251
+        // stolen from http://stackoverflow.com/questions/3354893/how-can-i-convert-a-datetime-to-the-number-of-seconds-since-1970
  252
+        public static DateTime ConvertFromUnixTimestamp(double timestamp)
  253
+        {
  254
+            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
  255
+            return origin.AddSeconds(timestamp);
  256
+        }
  257
+
  258
+        public static double ConvertToUnixTimestamp(DateTime date)
  259
+        {
  260
+            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
  261
+            TimeSpan diff = date.ToUniversalTime() - origin;
  262
+            return Math.Floor(diff.TotalSeconds);
  263
+        }
  264
+
  265
+
45 266
     }
46 267
 }
106  windows/Blink1Control/Blink1Control/MySettings.cs
... ...
@@ -0,0 +1,106 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+using System.Threading.Tasks;
  6
+using System.Xml.Serialization;
  7
+using System.IO;
  8
+
  9
+namespace Blink1Control
  10
+{
  11
+    /*
  12
+    using System.Collections.Generic;
  13
+    using System.Configuration;
  14
+
  15
+    /// <summary>
  16
+    /// Persistent store for my parameters.
  17
+    /// </summary>
  18
+    public class MySettings : ApplicationSettingsBase
  19
+    {
  20
+        /// <summary>
  21
+        /// The instance lock.
  22
+        /// </summary>
  23
+        private static readonly object InstanceLock = new object();
  24
+
  25
+        /// <summary>
  26
+        /// The instance.
  27
+        /// </summary>
  28
+        private static MySettings instance;
  29
+
  30
+        /// <summary>
  31
+        /// Prevents a default instance of the <see cref="MySettings"/> class 
  32
+        /// from being created.
  33
+        /// </summary>
  34
+        private MySettings()
  35
+        {
  36
+            // don't need to do anything
  37
+        }
  38
+
  39
+        /// <summary>
  40
+        /// Gets the singleton.
  41
+        /// </summary>
  42
+        public static MySettings Instance
  43
+        {
  44
+            get
  45
+            {
  46
+                lock (InstanceLock)
  47
+                {
  48
+                    if (instance == null)
  49
+                    {
  50
+                        instance = new MySettings();
  51
+                    }
  52
+                }
  53
+
  54
+                return instance;
  55
+            }
  56
+        }
  57
+
  58
+        /// <summary>
  59
+        /// Gets or sets the parameters.
  60
+        /// </summary>
  61
+        [UserScopedSetting]
  62
+        [SettingsSerializeAs(SettingsSerializeAs.Binary)]
  63
+        public Dictionary<string, string> Parameters
  64
+        {
  65
+            get
  66
+            {
  67
+                return (Dictionary<string, string>)this["Parameters"];
  68
+            }
  69
+
  70
+            set
  71
+            {
  72
+                this["Parameters"] = value;
  73
+            }
  74
+        }
  75
+    }
  76
+     */
  77
+
  78
+    public class MySettings
  79
+    {
  80
+        public Dictionary<string, string> Parameters { get; set; }
  81
+
  82
+        public MySettings()
  83
+        {
  84
+            Parameters = new Dictionary<string,string>();
  85
+        }
  86
+        public static void saveSettings(MySettings settings)
  87
+        {
  88
+            Console.WriteLine("saveSettings!");
  89
+            XmlSerializer mySerializer = new XmlSerializer(typeof(MySettings));
  90
+            StreamWriter myWriter = new StreamWriter("c:/prefs.xml");
  91
+            mySerializer.Serialize(myWriter, settings);
  92
+            myWriter.Close();
  93
+        }
  94
+
  95
+        public static MySettings loadSettings()
  96
+        {
  97
+            Console.WriteLine("loadSettings!");
  98
+            XmlSerializer mySerializer = new XmlSerializer(typeof(MySettings));
  99
+            FileStream myFileStream = new FileStream("c:/prefs.xml", FileMode.Open);
  100
+            MySettings settings = (MySettings)mySerializer.Deserialize(myFileStream);
  101
+            return settings;
  102
+        }
  103
+    }
  104
+    
  105
+}
  106
+
12  windows/Blink1Control/Blink1Control/Properties/Settings.Designer.cs
@@ -46,17 +46,5 @@ internal sealed partial class Settings : global::System.Configuration.Applicatio
46 46
                 this["hostId"] = value;
47 47
             }
48 48
         }
49  
-        
50  
-        [global::System.Configuration.UserScopedSettingAttribute()]
51  
-        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
52  
-        [global::System.Configuration.DefaultSettingValueAttribute("hello")]
53  
-        public string todtest {
54  
-            get {
55  
-                return ((string)(this["todtest"]));
56  
-            }
57  
-            set {
58  
-                this["todtest"] = value;
59  
-            }
60  
-        }
61 49
     }
62 50
 }
3  windows/Blink1Control/Blink1Control/Properties/Settings.settings
@@ -8,8 +8,5 @@
8 8
     <Setting Name="hostId" Type="System.String" Scope="User">
9 9
       <Value Profile="(Default)">00000000</Value>
10 10
     </Setting>
11  
-    <Setting Name="todtest" Type="System.String" Scope="User">
12  
-      <Value Profile="(Default)">hello</Value>
13  
-    </Setting>
14 11
   </Settings>
15 12
 </SettingsFile>
263  windows/Blink1Control/Blink1Control/blink1ControlServer.cs
@@ -11,38 +11,38 @@
11 11
 using System.Diagnostics;
12 12
 using System.Configuration;
13 13
 using System.Globalization;
14  
-using System.Net;
  14
+using System.Collections.ObjectModel;
15 15
 using MiniHttpd;
16 16
 using Newtonsoft.Json;
17 17
 using Blink1Lib;
18  
-using System.Collections.ObjectModel;
19  
-using System.Text.RegularExpressions;
20 18
 
21 19
 
22 20
 namespace Blink1Control
23 21
 {
24  
-    class Blink1Server
  22
+    public class Blink1Server
25 23
     {
26 24
         static int httpPortDefault = 8934;
27  
-        static string iftttEventUrl = "http://api.thingm.com/blink1/events";
28  
-        static float iftttUpdateInterval = 15.0F;
29  
-        static float urlUpdateInterval = 15.0F;
  25
+        public static string iftttEventUrl = "http://api.thingm.com/blink1/events";
  26
+        public static float iftttUpdateInterval = 15.0F;
  27
+        public static float urlUpdateInterval = 15.0F;
30 28
 
31 29
         HttpWebServer bhI = new HttpWebServer( httpPortDefault );
32 30
         //JTokenWriter jtw = new JTokenWriter();
33  
-        Blink1 blink1 = new Blink1();
  31
+        static Blink1 Sblink1 = new Blink1();
34 32
 
35 33
         Dictionary<string, Blink1Input> inputs   = new Dictionary<string, Blink1Input>();
36 34
         Dictionary<string, Blink1Pattern> patterns = new Dictionary<string, Blink1Pattern>();
37 35
 
38  
-        // constructor
39  
-        public Blink1Server()
40  
-        {
41  
-            inputs = new Dictionary<string, Blink1Input>();
42  
-            patterns = new Dictionary<string, Blink1Pattern>();
  36
+        static JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
43 37
 
  38
+        public void loadSettings()
  39
+        {
  40
+            // we assume hostId is always set, it defaults to "00000000" on fresh installation
  41
+            // (or we could not set it in Properties and check for KeyNotFoundException)
44 42
             blink1.hostId = (string)Properties.Settings.Default["hostId"];
45  
-            
  43
+
  44
+            Console.WriteLine("blink1.hostId:" + blink1.hostId);
  45
+
46 46
             blink1.regenerateBlink1Id();
47 47
 
48 48
             //MySettings.Instance.Parameters["foo"] = "bar";
@@ -75,7 +75,27 @@ public Blink1Server()
75 75
                 Console.WriteLine(Properties.Settings.Default.TheInputs.Count.ToString());
76 76
             }
77 77
 */
  78
+        }
  79
+
  80
+        public void saveSettings()
  81
+        {
  82
+            Properties.Settings.Default["hostId"] = blink1.hostId;
  83
+            Properties.Settings.Default.Save();
  84
+        }
  85
+
  86
+        // constructor
  87
+        public Blink1Server()
  88
+        {
  89
+            inputs = new Dictionary<string, Blink1Input>();
  90
+            patterns = new Dictionary<string, Blink1Pattern>();
  91
+
  92
+            loadSettings();
  93
+
78 94
             Console.WriteLine("Blink1Server!");
  95
+            Console.WriteLine("Running on port "+httpPortDefault);
  96
+            Console.WriteLine("blink1Id:"+blink1Id);
  97
+
  98
+            saveSettings();
79 99
 
80 100
             try {
81 101
                 VirtualDirectory root = new VirtualDirectory();
@@ -88,61 +108,68 @@ public Blink1Server()
88 108
 
89 109
                 // FIXME: the below is completely gross, how to do HTTP routing in .NET?
90 110
                 Blink1JSONFile id = new Blink1JSONFile("id", blink1dir, this);
91  
-                id.GetStringResponse = blink1Id;
  111
+                id.GetStringResponse = Ublink1Id;
92 112
                 blink1dir.AddFile(id);   //add a virtual file for each json method
93 113
 
94 114
                 Blink1JSONFile enumerate = new Blink1JSONFile("enumerate", blink1dir, this);
95  
-                enumerate.GetStringResponse = blink1Enumerate;
  115
+                enumerate.GetStringResponse = Ublink1Enumerate;
96 116
                 blink1dir.AddFile(enumerate);   //add a virtual file for each json method
97 117
 
98 118
                 Blink1JSONFile regen = new Blink1JSONFile("regenerateblink1id", blink1dir, this);
99  
-                regen.GetStringResponse = blink1RegenerateBlink1Id;
  119
+                regen.GetStringResponse = Ublink1RegenerateBlink1Id;
100 120
                 blink1dir.AddFile(regen);
101 121
 
102 122
                 Blink1JSONFile fadeToRGB = new Blink1JSONFile("fadeToRGB", blink1dir, this);
103  
-                fadeToRGB.GetStringResponse = blink1FadeToRGB;
  123
+                fadeToRGB.GetStringResponse = Ublink1FadeToRGB;
104 124
                 blink1dir.AddFile(fadeToRGB);
105 125
 
106 126
                 Blink1JSONFile on = new Blink1JSONFile("on", blink1dir, this);
107  
-                on.GetStringResponse = blink1On;
  127
+                on.GetStringResponse = Ublink1On;
108 128
                 blink1dir.AddFile(on);
109 129
 
110 130
                 Blink1JSONFile off = new Blink1JSONFile("off", blink1dir, this);
111  
-                off.GetStringResponse = blink1Off;
  131
+                off.GetStringResponse = Ublink1Off;
112 132
                 blink1dir.AddFile(off);
113 133
 
  134
+                Blink1JSONFile lastColor = new Blink1JSONFile("lastColor", blink1dir, this);
  135
+                lastColor.GetStringResponse = Ublink1LastColor;
  136
+                blink1dir.AddFile(lastColor);
  137
+
114 138
                 Blink1JSONFile pattern = new Blink1JSONFile("pattern", blink1dir, this);
115  
-                pattern.GetStringResponse = blink1Pattern;
  139
+                pattern.GetStringResponse = Ublink1Pattern;
116 140
                 blink1dir.AddFile(pattern);
117 141
 
118 142
                 Blink1JSONFile pattadd = new Blink1JSONFile("patternadd", blink1dir, this);
119  
-                pattadd.GetStringResponse = blink1PatternAdd;
  143
+                pattadd.GetStringResponse = Ublink1PatternAdd;
120 144
                 blink1dir.AddFile(pattadd);
121 145
 
122 146
                 Blink1JSONFile pattdel = new Blink1JSONFile("patterndel", blink1dir, this);
123  
-                pattdel.GetStringResponse = blink1PatternDel;
  147
+                pattdel.GetStringResponse = Ublink1PatternDel;
124 148
                 blink1dir.AddFile(pattdel);
125 149
 
126 150
                 Blink1JSONFile pattdelall = new Blink1JSONFile("patterndelall", blink1dir, this);
127  
-                pattdelall.GetStringResponse = blink1PatternDelAll;
  151
+                pattdelall.GetStringResponse = Ublink1PatternDelAll;
128 152
                 blink1dir.AddFile(pattdelall);
129 153
 
130 154
                 Blink1JSONFile input = new Blink1JSONFile("input", blink1dir, this);
131  
-                input.GetStringResponse = blink1Input;
  155
+                input.GetStringResponse = Ublink1Input;
132 156
                 blink1dir.AddFile(input);
133 157
 
134 158
                 Blink1JSONFile inputdel = new Blink1JSONFile("inputdel", blink1dir, this);
135  
-                inputdel.GetStringResponse = blink1InputDel;
  159
+                inputdel.GetStringResponse = Ublink1InputDel;
136 160
                 blink1dir.AddFile(inputdel);
137 161
 
138 162
                 Blink1JSONFile inputdelall = new Blink1JSONFile("inputdelall", blink1dir, this);
139  
-                inputdelall.GetStringResponse = blink1InputDelAll;
  163
+                inputdelall.GetStringResponse = Ublink1InputDelAll;
140 164
                 blink1dir.AddFile(inputdelall);
141 165
 
142 166
                 Blink1JSONFile inputurl = new Blink1JSONFile("inputurl", blink1dir, this);
143  
-                inputurl.GetStringResponse = blink1InputUrl;
  167
+                inputurl.GetStringResponse = Ublink1InputUrl;
144 168
                 blink1dir.AddFile(inputurl);
145 169
 
  170
+                Blink1JSONFile inputifttt = new Blink1JSONFile("inputifttt", blink1dir, this);
  171
+                inputifttt.GetStringResponse = Ublink1InputIfttt;
  172
+                blink1dir.AddFile(inputifttt);
146 173
                 /*
147 174
                                 Blink1JSONFile id2       = new Blink1JSONFile("id2", blink1dir, blink1);
148 175
                                 id2.GetStringResponse = blink1Id2;
@@ -170,11 +197,11 @@ public Blink1Server()
170 197
         //
171 198
         
172 199
         //    /blink1/id -- Display blink1_id and blink1 serial numbers (if any)
173  
-        static string blink1Id(HttpRequest request, Blink1Server blink1Server)
  200
+        static string Ublink1Id(HttpRequest request, Blink1Server blink1Server)
174 201
         {
175 202
             Blink1 blink1 = blink1Server.blink1;
176 203
             Dictionary<string, object> result = new Dictionary<string, object>();
177  
-            result.Add("blink1_id", blink1.getBlink1Id());
  204
+            result.Add("blink1_id", blink1.blink1Id);
178 205
             List<string> serialnums = new List<string>();
179 206
             for( int i=0; i < blink1.getCachedCount(); i++ ) {  // FIXME: surely a smarter way to do this
180 207
                 serialnums.Add( blink1.getCachedSerial(i) );
@@ -185,17 +212,17 @@ static string blink1Id(HttpRequest request, Blink1Server blink1Server)
185 212
         }
186 213
 
187 214
         //    /blink1/enumerate -- Re-enumerate and List available blink(1) devices
188  
-        static string blink1Enumerate(HttpRequest request, Blink1Server blink1Server)
  215
+        static string Ublink1Enumerate(HttpRequest request, Blink1Server blink1Server)
189 216
         {
190 217
             Blink1 blink1 = blink1Server.blink1;
191  
-            string blink1Id_old = blink1.getBlink1Id();
  218
+            string blink1Id_old = blink1.blink1Id;
192 219
 
193 220
             blink1.enumerate();
194 221
             blink1.regenerateBlink1Id();
195 222
 
196 223
             Dictionary<string, object> result = new Dictionary<string, object>();
197 224
             result.Add("blink1_id_old", blink1Id_old);
198  
-            result.Add("blink1_id", blink1.getBlink1Id());
  225
+            result.Add("blink1_id", blink1.blink1Id);
199 226
             List<string> serialnums = new List<string>();
200 227
             for (int i = 0; i < blink1.getCachedCount(); i++)
201 228
             {  // FIXME: surely a smarter way to do this
@@ -207,10 +234,10 @@ static string blink1Enumerate(HttpRequest request, Blink1Server blink1Server)
207 234
         }
208 235
 
209 236
         //    /blink1/regenerateblinkid -- Generate, save, and return new blink1_id
210  
-        static string blink1RegenerateBlink1Id(HttpRequest request, Blink1Server blink1Server)
  237
+        static string Ublink1RegenerateBlink1Id(HttpRequest request, Blink1Server blink1Server)
211 238
         {
212 239
             Blink1 blink1 = blink1Server.blink1;
213  
-            string blink1Id_old = blink1.getBlink1Id();
  240
+            string blink1Id_old = blink1.blink1Id;
214 241
 
215 242
             blink1.hostId = null;
216 243
             blink1.enumerate();
@@ -218,7 +245,7 @@ static string blink1RegenerateBlink1Id(HttpRequest request, Blink1Server blink1S
218 245
 
219 246
             Dictionary<string, object> result = new Dictionary<string, object>();
220 247
             result.Add("blink1_id_old", blink1Id_old);
221  
-            result.Add("blink1_id", blink1.getBlink1Id());
  248
+            result.Add("blink1_id", blink1.blink1Id);
222 249
             List<string> serialnums = new List<string>();
223 250
             for (int i = 0; i < blink1.getCachedCount(); i++)
224 251
             {  // FIXME: surely a smarter way to do this
@@ -230,12 +257,13 @@ static string blink1RegenerateBlink1Id(HttpRequest request, Blink1Server blink1S
230 257
         }
231 258
 
232 259
         //    /blink1/fadeToRGB -- Send fadeToRGB command to blink(1) with hex color & fade time
233  
-        static string blink1FadeToRGB(HttpRequest request, Blink1Server blink1Server)
  260
+        static string Ublink1FadeToRGB(HttpRequest request, Blink1Server blink1Server)
234 261
         {
235 262
             // FIXME: stop pattern player
236 263
             //NameValueCollection query = request.Query;
237 264
             string rgbstr  = request.Query.Get("rgb");
238 265
             string timestr = request.Query.Get("time");
  266
+            Console.WriteLine("rgb: " + rgbstr);
239 267
             if (rgbstr == null) rgbstr = "#000000";
240 268
             if (timestr == null) timestr = "0.1";   
241 269
             Color colr = ColorTranslator.FromHtml(rgbstr);
@@ -245,13 +273,13 @@ static string blink1FadeToRGB(HttpRequest request, Blink1Server blink1Server)
245 273
 
246 274
             Dictionary<string, object> result = new Dictionary<string, object>();
247 275
             result.Add("status", "fadeToRGB");
248  
-            result.Add("rgb", ColorTranslator.ToHtml(colr));
  276
+            result.Add("rgb", Blink1.colorToHexCode(colr) );
249 277
             result.Add("time", secs.ToString("F2", CultureInfo.InvariantCulture));
250 278
             return JsonConvert.SerializeObject(result, Formatting.Indented);
251 279
         }
252 280
 
253 281
         //    /blink1/on -- Stop pattern playback and send fadeToRGB command to blink(1) with #FFFFFF & 0.1 sec fade time
254  
-        static string blink1On(HttpRequest request, Blink1Server blink1Server)
  282
+        static string Ublink1On(HttpRequest request, Blink1Server blink1Server)
255 283
         {
256 284
             blink1Server.fadeToRGB(0.1, Color.White);
257 285
 
@@ -261,7 +289,7 @@ static string blink1On(HttpRequest request, Blink1Server blink1Server)
261 289
         }
262 290
 
263 291
         //    /blink1/off -- Stop pattern playback and send fadeToRGB command to blink(1) with #000000 & 0.1 sec fade time
264  
-        static string blink1Off(HttpRequest request, Blink1Server blink1Server)
  292
+        static string Ublink1Off(HttpRequest request, Blink1Server blink1Server)
265 293
         {
266 294
             blink1Server.fadeToRGB(0.1, Color.Black);
267 295
 
@@ -270,12 +298,22 @@ static string blink1Off(HttpRequest request, Blink1Server blink1Server)
270 298
             return JsonConvert.SerializeObject(result, Formatting.Indented);
271 299
         }
272 300
 
  301
+        //    /blink1/lastColor -- Return the last color command sent to blink(1)
  302
+        static string Ublink1LastColor(HttpRequest request, Blink1Server blink1Server)
  303
+        {
  304
+            Dictionary<string, object> result = new Dictionary<string, object>();
  305
+            result.Add("status", "lastColor");
  306
+            result.Add("lastColor", blink1Server.blink1.lastColorString);
  307
+            return JsonConvert.SerializeObject(result, Formatting.Indented);
  308
+        }
  309
+
  310
+
273 311
         // -----------------------------------------------------------------------------------------------
274 312
         // color patterns url handling
275 313
         //
276 314
 
277 315
         //    /blink1/pattern/ -- List saved color patterns
278  
-        static string blink1Pattern(HttpRequest request, Blink1Server blink1Server)
  316
+        static string Ublink1Pattern(HttpRequest request, Blink1Server blink1Server)
279 317
         {
280 318
             Dictionary<string, object> result = new Dictionary<string, object>();
281 319
             result.Add("status", "pattern results");
@@ -284,7 +322,7 @@ static string blink1Pattern(HttpRequest request, Blink1Server blink1Server)
284 322
         }
285 323
 
286 324
         //    /blink1/pattern/add -- Add color pattern to color pattern list
287  
-        static string blink1PatternAdd(HttpRequest request, Blink1Server blink1Server)
  325
+        static string Ublink1PatternAdd(HttpRequest request, Blink1Server blink1Server)
288 326
         {
289 327
             string pname      = request.Query.Get("pname");
290 328
             string patternstr = request.Query.Get("pattern");
@@ -299,7 +337,7 @@ static string blink1PatternAdd(HttpRequest request, Blink1Server blink1Server)
299 337
         }
300 338
 
301 339
         //    /blink1/pattern/del -- Remove color pattern from color pattern list
302  
-        static string blink1PatternDel(HttpRequest request, Blink1Server blink1Server)
  340
+        static string Ublink1PatternDel(HttpRequest request, Blink1Server blink1Server)
303 341
         {
304 342
             string pname = request.Query.Get("pname");
305 343
             string statusstr = "no pattern by that name";
@@ -317,7 +355,7 @@ static string blink1PatternDel(HttpRequest request, Blink1Server blink1Server)
317 355
         }
318 356
 
319 357
         //    /blink1/pattern/delall -- Remove all color patterns from color pattern list
320  
-        static string blink1PatternDelAll(HttpRequest request, Blink1Server blink1Server)
  358
+        static string Ublink1PatternDelAll(HttpRequest request, Blink1Server blink1Server)
321 359
         {
322 360
             foreach (KeyValuePair<string, Blink1Pattern> kvp in blink1Server.patterns) {
323 361
                 kvp.Value.stop();
@@ -329,7 +367,7 @@ static string blink1PatternDelAll(HttpRequest request, Blink1Server blink1Server
329 367
         }
330 368
 
331 369
         //    /blink1/pattern/play -- Play/test a specific color pattern
332  
-        static string blink1PatternPlay(HttpRequest request, Blink1Server blink1Server)
  370
+        static string Ublink1PatternPlay(HttpRequest request, Blink1Server blink1Server)
333 371
         {
334 372
             string pname = request.Query.Get("pname");
335 373
             string statusstr = "no pattern by that name";
@@ -347,7 +385,7 @@ static string blink1PatternPlay(HttpRequest request, Blink1Server blink1Server)
347 385
         }
348 386
 
349 387
         //    /blink1/pattern/stop -- Stop a pattern playback, for a given pattern or all patterns
350  
-        static string blink1PatternStop(HttpRequest request, Blink1Server blink1Server)
  388
+        static string Ublink1PatternStop(HttpRequest request, Blink1Server blink1Server)
351 389
         {
352 390
             string pname = request.Query.Get("pname");
353 391
             string statusstr = "no pattern by that name";
@@ -368,7 +406,7 @@ static string blink1PatternStop(HttpRequest request, Blink1Server blink1Server)
368 406
         //
369 407
 
370 408
         //    /blink1/input/ -- List configured inputs, enable or disable input watching
371  
-        static string blink1Input(HttpRequest request, Blink1Server blink1Server)
  409
+        static string Ublink1Input(HttpRequest request, Blink1Server blink1Server)
372 410
         {
373 411
             Dictionary<string, object> result = new Dictionary<string, object>();
374 412
             result.Add("status", "input results");
@@ -376,7 +414,7 @@ static string blink1Input(HttpRequest request, Blink1Server blink1Server)
376 414
             return JsonConvert.SerializeObject(result, Formatting.Indented);
377 415
         }
378 416
         //    /blink1/input/del -- Remove a configured input
379  
-        static string blink1InputDel(HttpRequest request, Blink1Server blink1Server)
  417
+        static string Ublink1InputDel(HttpRequest request, Blink1Server blink1Server)
380 418
         {
381 419
             string iname = request.Query.Get("iname");
382 420
             string statusstr = "no input by that name";
@@ -394,7 +432,7 @@ static string blink1InputDel(HttpRequest request, Blink1Server blink1Server)
394 432
         }
395 433
 
396 434
         //    /blink1/input/delall -- Remove all configured inputs
397  
-        static string blink1InputDelAll(HttpRequest request, Blink1Server blink1Server)
  435
+        static string Ublink1InputDelAll(HttpRequest request, Blink1Server blink1Server)
398 436
         {
399 437
             foreach (KeyValuePair<string, Blink1Input> kvp in blink1Server.inputs) {
400 438
                 kvp.Value.stop();
@@ -406,7 +444,7 @@ static string blink1InputDelAll(HttpRequest request, Blink1Server blink1Server)
406 444
         }
407 445
 
408 446
         //    /blink1/input/url -- Add and Start URL watcher on given URL
409  
-        static string blink1InputUrl(HttpRequest request, Blink1Server blink1Server)
  447
+        static string Ublink1InputUrl(HttpRequest request, Blink1Server blink1Server)
410 448
         {
411 449
             string pname = request.Query.Get("pname");
412 450
             string iname = request.Query.Get("iname");
@@ -421,8 +459,10 @@ static string blink1InputUrl(HttpRequest request, Blink1Server blink1Server)
421 459
             if( url != null && iname != null ) {
422 460
                 statusstr = "input url";
423 461
                 input = new Blink1Input(iname, "url", url, null, null);
  462
+                input.blink1Server = blink1Server;
424 463
                 input.pname = pname;
425  
-                blink1Server.updateUrlInput(input);
  464
+
  465
+                input.updateUrlInput();
426 466
                 if (!testmode) {
427 467
                     blink1Server.inputs[iname] = input; // NOTE: this replaces input if already exists
428 468
                 }
@@ -431,23 +471,43 @@ static string blink1InputUrl(HttpRequest request, Blink1Server blink1Server)
431 471
             Dictionary<string, object> result = new Dictionary<string, object>();
432 472
             result.Add("status", statusstr);
433 473
             result.Add("input", input);
434  
-            return JsonConvert.SerializeObject(result, Formatting.Indented);
  474
+            return JsonConvert.SerializeObject(result, Formatting.Indented, jsonSerializerSettings);
435 475
         }
436 476
 
437 477
         //    /blink1/input/ifttt -- Add and Start watching messages from IFTTT webservice
438  
-        static string blink1InputIfttt(HttpRequest request, Blink1Server blink1Server)
  478
+        static string Ublink1InputIfttt(HttpRequest request, Blink1Server blink1Server)
439 479
         {
440 480
             string pname = request.Query.Get("pname");
441 481
             string iname = request.Query.Get("iname");
442  
-            string url = request.Query.Get("url");
  482
+            string rulename = request.Query.Get("arg1");
443 483
             string test = request.Query.Get("test");
444 484
             if (pname == null) pname = iname;
445 485
             Boolean testmode = (test == null) ? false : (test.Equals("on") || test.Equals("true"));
446 486
 
447  
-            // FIXME: insert magic here
  487
+            string statusstr = "must specifiy 'iname' and 'arg1' (rulename)";
  488
+
  489
+            Blink1Input input = null;
  490
+            if (rulename != null && iname != null) {
  491
+                input = new Blink1Input(iname, "ifttt", rulename, null, null);
  492
+                input.blink1Server = blink1Server;
  493
+                input.pname = pname;
  494
+
  495
+                // FIXME: insert magic here
  496
+                //if (testmode) {
  497
+                Blink1Input.getIftttResponse(false);
  498
+                //}
  499
+
  500
+                input.updateIftttInput();
  501
+                if (!testmode) {
  502
+                    blink1Server.inputs[iname] = input; // NOTE: this replaces input if already exists
  503
+                }
  504
+
  505
+                statusstr = "input ifttt";
  506
+            }
448 507
 
449 508
             Dictionary<string, object> result = new Dictionary<string, object>();
450  
-            result.Add("status", "not implemented yet");
  509
+            result.Add("status", statusstr);
  510
+            result.Add("input", input);
451 511
             return JsonConvert.SerializeObject(result, Formatting.Indented);
452 512
         }
453 513
 
@@ -463,62 +523,10 @@ public void updateInputs()
463 523
         {
464 524
             foreach (var pair in inputs) {
465 525
                 Blink1Input input = pair.Value;
466  
-                if (input.type.Equals("ifttt")) { // FIXME: there's totally a better way of doing this
467  
-                    updateIftttInput(input);
468  
-                }
469  
-                else if (input.type.Equals("url")) {
470  
-                    updateUrlInput(input);
471  
-                }
472  
-                else if (input.type.Equals("file")) {
473  
-                }
474  
-                else if (input.type.Equals("script")) {
475  
-                }
476  
-            }
477  
-        }
478  
-
479  
-        public void updateIftttInput(Blink1Input input)
480  
-        {
481  
-
482  
-        }
483  
-
484  
-        public void updateUrlInput(Blink1Input input)
485  
-        {
486  
-            string url = input.arg1;
487  
-
488  
-            string resp = getContentOfUrl(url);
489  
-            if (resp == null) {
490  
-                input.lastVal = "bad url";
491  
-                return;
492  
-            }
493  
-            string patternstr = parsePatternOrColorString(resp);
494  
-
495  
-            if (patternstr != null && !patternstr.Equals(input.lastVal)) {
496  
-                input.lastVal = patternstr;
497  
-                playPattern(patternstr);
  526
+                input.update();
498 527
             }
499 528
         }
500 529
 
501  
-        /// <summary>
502  
-        /// 
503  
-        /// </summary>
504  
-        /// <param name="url"></param>
505  
-        /// <returns></returns>
506  
-        public string getContentOfUrl(string url)
507  
-        {
508  
-            string content = null;
509  
-            WebClient webclient = new WebClient();
510  
-            try {
511  
-                content = webclient.DownloadString(url);
512  
-            }
513  
-            catch (WebException we) {
514  
-                // WebException.Status holds useful information
515  
-                Console.WriteLine(we.Message + "\n" + we.Status.ToString());
516  
-            }
517  
-            catch (NotSupportedException ne) {
518  
-                Console.WriteLine(ne.Message);   // other errors
519  
-            } 
520  
-            return content;
521  
-        }
522 530
 
523 531
         // ----------------------------------------------------------------------------------------
524 532
         // pattern handling
@@ -551,30 +559,6 @@ public Boolean playPattern(string pname)
551 559
         }
552 560
 
553 561
         /// <summary>
554  
-        /// 
555  
-        /// </summary>
556  
-        /// <param name="str"></param>
557  
-        /// <returns></returns>
558  
-        public string parsePatternOrColorString(string str)
559  
-        {
560  
-            // read color pattern, can be of form: pattern: "my pattern"
561  
-            Regex patternregex = new Regex(@"\""*pattern\""*:\s*\""(.+?)\""");
562  
-            Match matchp = patternregex.Match(str);
563  
-            if( matchp.Success ) { 
564  
-                return matchp.Groups[1].Value;
565  
-            }
566  
-            //
567  
-            Regex hexcoderegex = new Regex(@"(#[A-Fa-f0-9]{6})");
568  
-            Match matchc = hexcoderegex.Match(str.ToUpper());
569  
-            if( matchc.Success ) {
570  
-                return matchc.Groups[1].Value;
571  
-            }
572  
-
573  
-            //Color c = ColorTranslator.FromHtml(str);
574  
-            return null;
575  
-        }
576  
-
577  
-        /// <summary>
578 562
         /// Same as Blink1.fadeToRGB, but does an open/close around it.
579 563
         /// Called by Blink1Patterns to send colors to blink(1)
580 564
         /// </summary>
@@ -586,8 +570,9 @@ public void fadeToRGB(double secs, Color c)
586 570
             blink1.close();
587 571
         }
588 572
 
  573
+        public Blink1 blink1 { get { return Blink1Server.Sblink1; } private set { } }
  574
+        public static string blink1Id { get { return Blink1Server.Sblink1.blink1Id; } }
589 575
 
590  
-//    /blink1/lastColor -- Return the last color command sent to blink(1)
591 576
 
592 577
 
593 578
 //    /blink1/input/file -- Add and Start file watcher on given filepath
@@ -601,18 +586,20 @@ public void fadeToRGB(double secs, Color c)
601 586
 
602 587
 //    /blink1/input/netload -- Start network load watching input
603 588
 
604  
-
605  
-
606 589
         // testing
607  
-        static string blink1Id2(HttpRequest request, Blink1 blink1)//example
  590
+        static string Ublink1Id2(HttpRequest request, Blink1Server blink1Server)//example
608 591
         {
609  
-            Properties.Settings.Default["blink1Id"] = blink1.getBlink1Id();
  592
+            //Properties.Settings.Default["blink1Id"] = blink1Server.blink1Id;
610 593
             Properties.Settings.Default.Save();
611 594
             //Settings.Default["blink1Id"] = blink1.getBlink1Id();
612 595
             //Settings.Default.Save(); // Saves settings in application configuration file
613 596
             return @"\n\n{ suck it }\n\n";
614 597
         }
615 598
 
  599
+        //
  600
+        // ---------------------------------------------------------------------------------------
  601
+        //
  602
+
616 603
         /// <summary>
617 604
         /// Weirdass wrapper for binding urls to funcblocks, why do we need this?
618 605
         /// </summary>
49  windows/Blink1Lib/Blink1Lib/Blink1.cs
@@ -10,7 +10,21 @@ namespace Blink1Lib
10 10
 {
11 11
     public class Blink1
12 12
     {
13  
-        string blink1Id;
  13
+        /// <summary>
  14
+        /// Returns the blink(1) uid (aka IFTTT key) 
  15
+        /// </summary>
  16
+        /// <returns>blink1_uid</returns>
  17
+        public string blink1Id { get; private set; }
  18
+        /// <summary>
  19
+        /// hostid is a component of the blink1Id (aka IFTT key) 
  20
+        /// </summary>
  21
+        public string hostId { get; set; }
  22
+        /// <summary>
  23
+        /// 
  24
+        /// </summary>
  25
+        public Color lastColor { get; private set; }
  26
+
  27
+        public string lastColorString { get { return Blink1.colorToHexCode(lastColor); } }
14 28
 
15 29
         static void Main(string[] args)
16 30
         {
@@ -23,25 +37,7 @@ static void Main(string[] args)
23 37
         public Blink1()
24 38
         {
25 39
             enumerate();
26  
-        }
27  
-
28  
-        /// <summary>
29  
-        /// hostid is a component of the blink1Id (aka IFTT key) 
30  
-        /// </summary>
31  
-        public string hostId { get; set; }
32  
-
33  
-        /// <summary>
34  
-        /// Returns the blink(1) uid (aka IFTTT key) 
35  
-        /// </summary>
36  
-        /// <returns>blink1_uid</returns>
37  
-        public string getBlink1Id()
38  
-        {