internal void REST_POST(string url, string content, int len) { HttpWebRequest request; HttpWebResponse response; string rest_url; int code; rest_url = Base + url; WFLogging.Debug(rest_url); request = (HttpWebRequest)HttpWebRequest.Create(rest_url); request.UserAgent = "WFNodeServer"; if (AuthRequired) { request.Headers.Add("Authorization", Authorize()); } request.Proxy = null; request.KeepAlive = false; request.Method = "POST"; request.ContentLength = len; request.ContentType = "application/xml"; Stream datastream = request.GetRequestStream(); datastream.Write(Encoding.ASCII.GetBytes(content), 0, len); datastream.Close(); try { response = (HttpWebResponse)request.GetResponse(); code = (int)response.StatusCode; response.Close(); } catch (Exception ex) { WFLogging.Error(ex.Message); } }
private static void ProcessWSData(string json) { if (json.Contains("obs_air")) { WeatherFlowNS.NS.udp_client.WSObservations(json); } else if (json.Contains("obs_sky")) { WeatherFlowNS.NS.udp_client.WSObservations(json); } else if (json.Contains("rapid_wind")) { WeatherFlowNS.NS.udp_client.RapidWindEvt(json); } else if (json.Contains("evt_strike")) { WeatherFlowNS.NS.udp_client.LigtningStrikeEvt(json); } else if (json.Contains("evt_precip")) { } else if (json.Contains("ack")) { } else { WFLogging.Error("Unknown type of WebSocket packet"); WFLogging.Error(json); } }
internal static void LogPage(HttpListenerContext context) { byte[] page; // Process changes if (context.Request.ContentLength64 > 0) { int len = (int)context.Request.ContentLength64; byte[] post = new byte[1024]; context.Request.InputStream.Read(post, 0, len); string resp = Encoding.Default.GetString(post); resp = resp.Substring(0, len); foreach (string item in resp.Split('&')) { string[] pair = item.Split('='); switch (pair[0]) { case "sLogLevel": int l = 0; int.TryParse(pair[1], out l); if (l != WF_Config.LogLevel) { WF_Config.LogLevel = l; WFLogging.Level = (LOG_LEVELS)l; WeatherFlowNS.SaveConfiguration(); } break; case "Save": // Look up value of filename and save log to that file string fname = element(resp, "filename"); try { using (StreamWriter sw = new StreamWriter(fname)) { sw.Write(WFLogging.ToString()); } } catch (Exception ex) { WFLogging.Error(ex.Message); } break; case "Clear": WFLogging.Clear(); break; } } } page = ASCIIEncoding.ASCII.GetBytes(MakeLog()); context.Response.ContentType = "text/html"; context.Response.ContentLength64 = page.Length; context.Response.AddHeader("Date", DateTime.Now.ToString("r")); context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.OutputStream.Write(page, 0, page.Length); context.Response.OutputStream.Flush(); context.Response.OutputStream.Close(); context.Response.Close(); }
internal static void UploadFile(rest Rest, int profile, string resname, string type) { Assembly assembly; string resourceName; string contents = ""; // Load resource into string assembly = Assembly.GetExecutingAssembly(); resourceName = "WFNodeServer.nodesetup." + resname; using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { contents = reader.ReadToEnd(); } } if (contents.Length > 0) { // Send to ISY // This is just a http post to /rest/ns/profile/<profile>/upload/<type>/<filename> Rest.AuthRequired = true; try { Rest.REST_POST("ns/profile/" + profile.ToString() + "/upload/" + type + "/" + resname, contents, contents.Length); } catch (Exception ex) { WFLogging.Error(resname + " upload failed: " + ex.Message); } } }
internal bool GetStationMeta(string station_id) { string resp; RootObject root; JavaScriptSerializer serializer = new JavaScriptSerializer(); resp = WFRest.REST("/swd/rest/stations/" + station_id + "?api_key=6c8c96f9-e561-43dd-b173-5198d8797e0a"); if (resp == "") { return(false); } if (resp.Contains("ERROR")) { return(false); } if (resp != "") { try { root = serializer.Deserialize <RootObject>(resp); if (root.stations.Count == 0) { return(false); } Latitude = root.stations[0].latitude; Longitude = root.stations[0].longitude; //Console.WriteLine("latitude: " + root.stations[0].latitude.ToString()); //Console.WriteLine("longitude: " + root.stations[0].longitude.ToString()); //Console.WriteLine("elevation: " + root.stations[0].station_meta.elevation.ToString()); foreach (Device device in root.stations[0].devices) { // Types that we're interested in are: // SK - sky // AR - air //Console.WriteLine("Sensor: " + device.device_id.ToString() + " is " + device.device_type + " -agl = " + device.device_meta.agl.ToString()); if (device.device_type == "AR") { Elevation = root.stations[0].station_meta.elevation + device.device_meta.agl; Air = device.device_id; AirSN = device.serial_number; } else if (device.device_type == "SK") { Sky = device.device_id; SkySN = device.serial_number; } } } catch (Exception ex) { WFLogging.Error("Error: " + ex.Message); return(false); } } return(true); }
internal void PrecipitationEvt(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { // evt[0] = timestamp PreciptObj = serializer.Deserialize <PreciptData>(json); WeatherFlowNS.NS.RaiseRainEvent(this, new RainEventArgs(PreciptObj)); } catch (Exception ex) { WFLogging.Error("Failed to deserialize precipitation event: " + ex.Message); } }
private string MakeRequest(string rest_url) { HttpWebRequest request; HttpWebResponse response; int code; DateTime start = DateTime.Now; string xml = ""; request = (HttpWebRequest)HttpWebRequest.Create(rest_url); request.UserAgent = "WFNodeServer"; if (AuthRequired) { request.Headers.Add("Authorization", Authorize()); } request.Proxy = null; request.ServicePoint.ConnectionLimit = 10; request.Timeout = 4000; request.KeepAlive = true; //request.Pipelined = true; // Read data from the stream try { response = (HttpWebResponse)request.GetResponse(); code = (int)response.StatusCode; if (code != 200) { response.Close(); throw new RestException(code, response.StatusDescription); } if (response.ContentLength == 0) { return(""); } // sucess with content xml = ChunkedRead(response); response.Close(); } catch (WebException ex) { if (ex.Status == WebExceptionStatus.ProtocolError) { throw new RestException(400, ex.Message); } WFLogging.Error("GetResponse thew exception: " + ex.Status.ToString()); throw new RestException(0, ex.Message); } stats.RequestTime = DateTime.Now.Subtract(start).TotalMilliseconds; stats.Count++; return(xml); }
internal void LigtningStrikeEvt(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { // evt[0] = timestamp // evt[1] = distance (km) // evt[2] = energy StrikeObj = serializer.Deserialize <StrikeData>(json); WeatherFlowNS.NS.RaiseLightningEvent(this, new LightningEventArgs(StrikeObj)); } catch (Exception ex) { WFLogging.Error("Failed to deserialize strike event: " + ex.Message); } }
// This is handling the SetParent service only at this point but could // be expanded to handle other ISY WSDL reuests if needed. internal void SendWSDLReqeust(string service, string parent, string node) { string url = "http://" + WF_Config.ISY + "/services"; HttpWebRequest request; HttpWebResponse response; string reqString = ""; reqString = "<? version=\'1.0\' encoding=\'utf-8\'?>"; reqString += "<s:Envelope>"; reqString += "<s:Body>"; reqString += "<u:" + service + " xmlns:u=\'urn:udi-com:service:X_Insteon_Lighting_Service:1\'>"; reqString += "<node>" + node + "</node>"; reqString += "<nodeType>1</nodeType>"; reqString += "<parent>" + parent + "</parent>"; reqString += "<parentType>1</parentType>"; reqString += "</u:" + service + ">"; reqString += "</s:Body>"; reqString += "</s:Envelope>"; reqString += "\r\n"; request = (HttpWebRequest)HttpWebRequest.Create(url); request.KeepAlive = true; request.Method = "POST"; request.ContentType = "text/xml; charset-utf-8"; request.Headers.Add("Authorization", Authorize()); request.Headers.Add("SOAPAction", "urn:udi-com:device:X_Insteon_Lighting_Service:1#" + service); Stream data = request.GetRequestStream(); data.Write(Encoding.ASCII.GetBytes(reqString), 0, reqString.Length); data.Flush(); data.Close(); response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode == HttpStatusCode.OK) { WFLogging.Log("Grouped " + node + " as a child of " + parent); } else { WFLogging.Error("Group of " + node + " under " + parent + "failed: " + response.StatusDescription); } response.Close(); return; }
// // Given a REST partial URL, make the connection and return // the XML response // internal string REST(string url) { string rest_url; string resp = ""; rest_url = Base + url; if (rest_url == "") { WFLogging.Error("ISY REST called with missing URL."); WFLogging.Error(" Does this mean there's no connection to an ISY?"); return(""); } lock (RateLimit) { WFLogging.Debug(rest_url); int retrys = 0; while (retrys < 5) { try { resp = MakeRequest(rest_url); break; } catch (RestException re) { if (re.Code != 0) { WFLogging.Error("REST request " + url + " failed " + re.Message); break; } else { // Timeout or failed to get result retrys++; if (retrys == 5) { WFLogging.Error("REST request " + url + " failed after 5 retries with " + re.Message); } Thread.Sleep(50 * retrys); } } } } return(resp); }
internal void RapidWindEvt(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { // evt[0] = timestamp // evt[1] = speed (m/s) // evt[2] = direction WindObj = serializer.Deserialize <WindData>(json); StationInfo si = wf_station.FindStationSky(WindObj.serial_number); if (si.rapid) { WeatherFlowNS.NS.RaiseRapidEvent(this, new RapidEventArgs(WindObj)); WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)WindObj.ob[0], WindObj.serial_number + "_r", DataType.WIND)); } } catch (Exception ex) { WFLogging.Error("Failed to deserialize rapid wind event: " + ex.Message); WFLogging.Error(json); } }
private bool AddStation(station_form a_form) { wf_station station = new wf_station(api_key); if (station.GetStationMeta(a_form.station_id.ToString()) || (a_form.station_id == 0)) { a_form.air_id = (a_form.air_id == 0) ? station.Air : a_form.air_id; a_form.sky_id = (a_form.sky_id == 0) ? station.Sky : a_form.sky_id; a_form.air_sn = (a_form.air_sn == "") ? station.AirSN : a_form.air_sn; a_form.sky_sn = (a_form.sky_sn == "") ? station.SkySN : a_form.sky_sn; a_form.elevation = (a_form.elevation == 0) ? station.Elevation : a_form.elevation; WeatherFlowNS.NS.AddStation(a_form.station_id, a_form.elevation, a_form.air_id, a_form.sky_id, a_form.remote, station.AirSN, station.SkySN, a_form.rapid); return(true); } else { WFLogging.Error("Failed to find station " + a_form.station_id.ToString()); cfg_file_status = "Station " + a_form.station_id.ToString() + " lookup failed."; } return(false); }
private void Listen() { using (listener = new HttpListener()) { try { listener.Prefixes.Add("http://*:" + port.ToString() + "/"); listener.Start(); } catch (Exception ex) { WFLogging.Error("Failed to start web server on port " + port.ToString()); WFLogging.Error(" Error was: " + ex.Message); return; } while (true) { try { HttpListenerContext context = listener.GetContext(); Process(context); } catch (Exception ex) { WFLogging.Error("Failed to process connection: " + ex.Message); } //Thread.Sleep(5000); } } }
// TODO: // How do we want to handle the device status? There should // be at least 2 devices present (Air & Sky). Do we create // a HS record for each and use that as a place to store the // device specific data? Or do we just create an internal // list of devivces? // // Whatever we do here we should probably do for the hub // as well (treat it as a third device). private void DeviceStatus(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { DeviceObj = serializer.Deserialize <DeviceData>(json); // Add event to update this info. try { WeatherFlowNS.NS.RaiseDeviceEvent(this, new WFNodeServer.DeviceEventArgs(DeviceObj)); } catch { WFLogging.Warning("Failed to process device event."); } WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)DeviceObj.timestamp, DeviceObj.serial_number + "_d", DataType.DEVICE)); //Console.WriteLine("Serial Number: " + DeviceObj.serial_number); //Console.WriteLine("Device Type: " + DeviceObj.type); //Console.WriteLine("Hub Serial Number: " + DeviceObj.hub_sn); //Console.WriteLine("timestamp: " + DeviceObj.timestamp.ToString()); //Console.WriteLine("uptime: " + DeviceObj.uptime.ToString()); //Console.WriteLine("Voltage: " + DeviceObj.voltage.ToString()); //Console.WriteLine("Firmware: " + DeviceObj.firmware_revision.ToString()); //Console.WriteLine("RSSI: " + DeviceObj.rssi.ToString()); //Console.WriteLine("Sensor status: " + DeviceObj.sensor_status.ToString()); if (Sensors.ContainsKey(DeviceObj.serial_number)) { Sensors[DeviceObj.serial_number] = DeviceObj; } else { Sensors.Add(DeviceObj.serial_number, DeviceObj); } } catch (Exception ex) { WFLogging.Error("Deserialization of device status failed: " + ex.Message); } }
private void HubStatus(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { HubObj = serializer.Deserialize <HubData>(json); WeatherFlowNS.NS.RaiseHubEvent(this, new WFNodeServer.HubEventArgs(HubObj)); WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)HubObj.timestamp, HubObj.serial_number, DataType.HUB)); //Console.WriteLine("Serial Number: " + HubObj.serial_number); //Console.WriteLine("Device Type: " + HubObj.type); //Console.WriteLine("Firmware: " + HubObj.firmware_revision.ToString()); //Console.WriteLine("uptime: " + HubObj.uptime.ToString()); //Console.WriteLine("RSSI: " + HubObj.rssi.ToString()); //Console.WriteLine("timestamp: " + HubObj.timestamp.ToString()); //Console.WriteLine("Reset Flags: " + HubObj.reset_flags); //Console.WriteLine("Stack: " + HubObj.stack); //Console.WriteLine("Sequence: " + HubObj.seq.ToString()); //Console.WriteLine("External File: " + HubObj.fs.ToString()); ValidHub = true; } catch (Exception ex) { WFLogging.Error("Deserialization of device status failed: " + ex.Message); } }
private void SkyObservations(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); try { SkyObj = serializer.Deserialize <SkyData>(json); SkyObj.valid = true; WFNodeServer.SkyEventArgs evnt = new SkyEventArgs(SkyObj); evnt.SetDaily = CalcDailyPrecipitation(); evnt.Raw = json; try { WeatherFlowNS.NS.RaiseSkyEvent(this, evnt); } catch (Exception ex) { WFLogging.Warning("Failed to process Sky event. " + ex.Message); } WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)SkyObj.obs[0][0].GetValueOrDefault(), SkyObj.serial_number, DataType.SKY)); } catch (Exception ex) { WFLogging.Error("Deserialization failed for sky data: " + ex.Message); WFLogging.Verbose(json); return; } }
// Look for UPNP broadcast messages from an ISY. If it finds // one, then use it. // // FIXME: How do we know this is the right ISY? What if there // is more than one on the network? internal static string IsyAutoDetect() { UdpClient listen_udp; IPAddress group_ip; IPEndPoint group_ep; string ip = ""; byte[] recv_data; int i; string buf = ""; int tries = 100; using (listen_udp = new UdpClient()) { listen_udp.ExclusiveAddressUse = false; listen_udp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); group_ip = IPAddress.Parse("239.255.255.250"); //group_ep = new IPEndPoint(IPAddress.Any, 1900); group_ep = new IPEndPoint(IPAddress.Any, 20034); try { listen_udp.Client.Bind(group_ep); } catch (Exception e) { WFLogging.Error("Failed to bind to broadcast address"); WFLogging.Error(e.Message); return(""); } listen_udp.EnableBroadcast = true; try { listen_udp.JoinMulticastGroup(group_ip); } catch (Exception e) { WFLogging.Error("Failed to join Multicast group: " + e.Message); //listen_udp.Close(); return(""); } // Set the timeout at 90 seconds. If we haven't received anything in // that time, we probably won't. listen_udp.Client.ReceiveTimeout = 90000; while ((ip == "")) { try { recv_data = listen_udp.Receive(ref group_ep); } catch { WFLogging.Error("Timed out trying to discover ISY."); return(""); } if (recv_data.Length != 0) { // Found somelthing buf = Encoding.ASCII.GetString(recv_data); // Now see if this is really an ISY if (buf.Contains("X_Insteon") == false) { if (--tries == 0) { WFLogging.Error("Failed to detect ISY on the network."); return(""); } } else { // This really is an ISY. Pull the location field // from the string. i = buf.IndexOf("LOCATION:"); if ((i > 0)) { ip = buf.Substring((i + 9)).Split('\r')[0]; } } } } listen_udp.DropMulticastGroup(group_ip); //listen_udp.Close(); } WFLogging.Log(("Found an ISY: " + ip)); return(ip); }
internal void Start() { byte[] buf = new byte[512]; int len; bool header = false; while (Started) { int retries = 0; WFLogging.Warning("Attempt to start already active WebSocket connection " + retries.ToString()); Thread.Sleep(1000); if (retries++ > 10) { WFLogging.Error("Giving up after 10 attempts."); return; } } client = new TcpClient(Host, Port); if (!client.Connected) { WFLogging.Error("Client not connected to " + Port); } WFLogging.Log("Starting communication with websocket server."); finished = false; Started = true; // Send header var seckeybytes = Encoding.UTF8.GetBytes(seckey); client.Client.Send(Encoding.ASCII.GetBytes("GET " + Path + " HTTP/1.1\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Host: " + Host + ":" + Port.ToString() + "\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Upgrade: websocket\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Connection: Upgrade\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Pragma: no-cache\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Origin: http://" + Host + "\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Cache-Control: no-cache\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Sec-WebSocket-Key: " + System.Convert.ToBase64String(seckeybytes) + "\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("Sec-WebSocket-Version: 13\r\n")); client.Client.Send(Encoding.ASCII.GetBytes("\r\n")); WFLogging.Info(" Waiting for handshake"); Thread.Sleep(100); // Receive handshake while (!header) { if (client.Client.Available > 0) { len = client.Client.Receive(buf, (client.Client.Available - 1), SocketFlags.None); if (len > 0) { string strbuf = Encoding.ASCII.GetString(buf, 0, len); // Just look for the a websock type response, ignore the rest of the headers if (strbuf.Contains("HTTP/1.1 101")) { header = true; } } } Thread.Sleep(500); } receive_thread = new Thread(new ThreadStart(ReceiveLoop)); receive_thread.IsBackground = true; receive_thread.Start(); }
private static void ReceiveCallback(IAsyncResult ar) { StateObject state = (StateObject)ar.AsyncState; TcpClient client = state.workSocket; int bytes_read = client.Client.EndReceive(ar); if (bytes_read > 0) { int bi = 0; byte[] mask = new byte[4]; if ((state.buffer[0] & 0x80) == 0x80) { int payload_type = state.buffer[0] & 0x0f; int payload_size = state.buffer[1] & 0x7f; int payload_masking = state.buffer[1] & 0x80; bi += 2; //Console.WriteLine("type = " + payload_type.ToString() + " mask = " + payload_masking.ToString() + " len = " + payload_size.ToString()); if (payload_size == 126) { payload_size = (state.buffer[bi++] << 8) + state.buffer[bi++]; //Console.WriteLine("extended size = " + payload_size.ToString()); } if (payload_masking == 0x80) { mask[0] = state.buffer[bi++]; mask[1] = state.buffer[bi++]; mask[2] = state.buffer[bi++]; mask[3] = state.buffer[bi++]; } if (bytes_read > payload_size) { for (int i = 0; i < payload_size; i++) { if (payload_masking == 0x80) { state.buffer[bi + i] = (byte)(state.buffer[bi + i] ^ mask[i % 4]); } } if (payload_type == 0x01) { // TODO: Text type payload so send it somewhere //Console.WriteLine("Payload: " + Encoding.ASCII.GetString(state.buffer, bi, payload_size)); //Program.RaiseEvent(new WSEventArgs(Encoding.ASCII.GetString(state.buffer, bi, payload_size))); ProcessWSData(Encoding.ASCII.GetString(state.buffer, bi, payload_size)); } else if (payload_type == 0x02) { // Binary payload } else if (payload_type == 0x00) { WFLogging.Error("Got a continuation opcode, currently not supported."); } else if (payload_type == 0x08) { // Close frame finished = true; SendMessage(client, Encoding.ASCII.GetString(state.buffer, bi, payload_size), 0x08); } else if (payload_type == 0x09) { // Ping so we need to pong SendMessage(client, Encoding.ASCII.GetString(state.buffer, bi, payload_size), 0x0A); } receiveDone.Set(); return; } else { // We need to read more data WFLogging.Info("Need more data to complete frame"); client.Client.BeginReceive(state.buffer, 0, StateObject.bufsize, 0, new AsyncCallback(ReceiveCallback), state); return; } } } receiveDone.Set(); }
private string MakeConfigPage() { string page; bool remote_configured = false; bool nodeserver_configured = false; foreach (StationInfo s in WF_Config.WFStationInfo) { if (s.remote && (s.air_id != 0 || s.sky_id != 0)) { remote_configured = true; } } if ((WF_Config.Profile > 0) && (WF_Config.Password != "") && (WF_Config.Username != "") && (WF_Config.Port > 0) && (WF_Config.ISY != "")) { nodeserver_configured = true; } page = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html\">\n"; page += "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n"; page += "<meta http-equiv=\"expires\" content=\"0\">\n"; page += "<meta http-equiv=\"pragma\" content=\"no-cache\">\n"; page += "<meta http-equiv=\"Content-Language\" content=\"en\">\n"; page += "<meta charset=\"UTF-8\">\n"; page += "<meta name=\"google\" content=\"notranslate\">\n"; page += "<style>\n"; page += " body { font-family: Sans-Serif; }\n"; page += "</style>\n"; page += "<title>WeatherFlow Nodeserver Web Interface</title>\n"; page += "</head><body>\n"; page += "<form name=\"root\" action=\"/config\" enctype=\"application/x-www-form-urlencoded\" method=\"post\">\n"; page += MakeMenu(); page += "<div style=\"margin: 20 auto; padding-bottom: 10px;\">\n"; page += "<div>\n"; page += "<table border=\"0\" width=\"600\" id=\"tblBody\" style=\"padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px\">\n"; page += "<tr><td colspan=\"3\" Class=\"sectionTitle\"><br><h2>Configuration</h2><br></td></tr>\n"; page += ConfigItem("Port", "webPort", WF_Config.Port.ToString(), 0); page += ConfigItem("ISY Address", "sAddress", WF_Config.ISY, 3); page += ConfigItem("ISY Username", "sUsername", WF_Config.Username, 1); page += ConfigItem("ISY Password", "sPassword", WF_Config.Password, 2); page += ConfigItem("Profile Number", "sProfile", WF_Config.Profile.ToString(), 0); page += ConfigBoolItem("Include Hub data", "sHub", WF_Config.Hub); page += ConfigBoolItem("Include Device data", "sDevice", WF_Config.Device); page += ConfigUnits("Set Units", "sUnits", WF_Config.Units); page += ConfigList("Set Logging Level", "sLogLevel", WF_Config.LogLevel); page += "<tr>\n"; page += "<td colspan=\"3\">"; if (nodeserver_configured) { page += "<form method=\"post\">"; page += "<input type=\"submit\" name=\"serverctl\" value=\" Restart Node Server \">"; page += " "; page += "<input type=\"submit\" name=\"serverctl\" value=\" Pause Node Server \">"; page += " "; page += "<input style=\"width: 120px; text-align: center; background-color: #e8e8e8;\" type=\"text\" name=\"status\" value=\""; page += (WeatherFlowNS.NS.udp_client.Active) ? "Running" : "Paused"; page += "\" readonly>"; page += " "; page += "<input type=\"submit\" name=\"upload\" value=\" Upload Profile Files \" placeholder=\"Upload files to the ISY\">"; page += "</form>"; } page += "</td>\n"; page += "</tr>\n"; page += "<tr><th colspan=\"3\"> </th></tr>\n"; page += "</table> </div> </form>\n"; page += "<div style=\"padding-left: 4px; padding-right: 4px; padding-top: 20px; padding-bottom: 1px\">\n"; page += "<table border=\"0\">\n"; page += "<tr><td colspan=\"8\"><h2>Station Configuration</h2></td></tr>\n"; page += "<tr><th>Station ID</th><th>Sky S/N</th><th>Air S/N</th><th>Elevation (meters)</th><th>Remote</th><th>Rapid</th><th> </th></tr>\n"; foreach (StationInfo s in WF_Config.WFStationInfo) { string placeholder = "Enter the weather station ID"; page += "<tr>"; page += "<form method=\"post\">"; if (s.station_id == 0) { page += "<td><input style=\"width:150px\" name = \"station_id\" readonly value=\"" + "Local UDP" + "\"></td>"; } else { page += "<td><input style=\"width:150px\" required placeholder=\"" + placeholder + "\" name = \"station_id\" type=\"number\" value=\"" + s.station_id.ToString() + "\"></td>"; } //page += "<td><input style=\"width:150px\" type=\"number\" readonly value=\"" + s.sky_id.ToString() + "\"></td>"; //page += "<td><input style=\"width:150px\" type=\"number\" readonly value=\"" + s.air_id.ToString() + "\"></td>"; page += "<td><input style=\"width:150px\" readonly value=\"" + s.sky_sn + "\"></td>"; page += "<td><input style=\"width:150px\" readonly value=\"" + s.air_sn + "\"></td>"; page += "<td><input style=\"width:150px\" name=\"elevation\" type=\"number\" step=\"any\" value=\"" + s.elevation.ToString() + "\"></td>"; page += "<td><input style=\"width:50px\" "; page += (s.remote) ? "checked" : "unchecked"; page += " type=\"checkbox\" name=\"remote\" value=\"1\"></td>"; page += "<td><input style=\"width:50px\" "; page += (s.rapid) ? "checked" : "unchecked"; page += " type=\"checkbox\" name=\"rapid\" value=\"2\"></td>"; page += "<td><input name=\"stationcfg\" type=\"submit\" value=\"Update\"></td>"; page += "<td><input name=\"stationcfg\" type=\"submit\" value=\"Delete\"></td>"; page += "</form></tr>\n"; } // Add input row page += "<form name=\"stations\" action=\"/config\" enctype=\"application/x-www-form-urlencoded\" method=\"post\">\n"; page += "<tr>"; page += "<td><input style=\"width:150px\" type=\"number\" step=\"any\" name=\"station_id\" value=\"\" required></td>\n"; //page += "<td><input style=\"width:150px\" type=\"number\" step=\"any\" name=\"sky_id\" value=\"\"></td>\n"; //page += "<td><input style=\"width:150px\" type=\"number\" step=\"any\" name=\"air_id\" value=\"\"></td>\n"; page += "<td><input style=\"width:150px\" name=\"sky_id\" value=\"\"></td>\n"; page += "<td><input style=\"width:150px\" name=\"air_id\" value=\"\"></td>\n"; page += "<td><input style=\"width:150px\" type=\"number\" step=\"any\" name=\"elevation\" value=\"\"></td>\n"; page += "<td><input style=\"width:50px\" type=\"checkbox\" name=\"remote\" value=\"1\"></td>\n"; page += "<td><input style=\"width:50px\" type=\"checkbox\" name=\"rapid\" value=\"1\"></td>\n"; page += "<td><input name=\"stationcfg\" type=\"submit\" value=\" Add \"></td>\n"; page += "</tr>"; page += "</form>\n"; page += "<tr>\n"; page += "<td colspan=\"7\">"; if (remote_configured) { page += "<form method=\"post\">\n"; page += "<input type=\"submit\" name=\"websocket\" value=\" Start WebSocket Client \">"; page += " "; page += "<input type=\"submit\" name=\"websocket\" value=\" Stop WebSocket Client \">"; page += " "; page += "<input style=\"width: 120px; text-align: center; background-color: #e8e8e8;\" type=\"text\" name=\"wsstatus\" value=\""; page += (WeatherFlowNS.NS.wsi.Started) ? "Running" : "Stopped"; page += "\" readonly>"; page += "</form>"; } page += "</td>\n"; page += "</tr>\n"; page += "</table>\n"; page += "</div>\n"; page += "<div style=\"padding-left: 4px; padding-right: 4px; padding-top: 20px; padding-bottom: 1px\">\n"; page += "<hr>\n"; page += "<div style=\"border: 1px solid; background-color: #D8D8D8; padding: 2px 2px 2px 2px\">"; page += "Status: " + cfg_file_status; page += "</div>"; page += "</div>"; // Display a table of nodes that were found on the ISY page += "<div style=\"padding-left: 4px; padding-right: 4px; padding-top: 20px; padding-bottom: 1px\">\n"; page += "<table width=\"450px\" border=\"1\">\n"; page += "<tr><th>Node Address</th><th>Node Type</th></tr>\n"; foreach (string n in WeatherFlowNS.NS.NodeList.Keys) { try { page += "<tr><td style=\"padding: 1px 1px 1px 5px;\">" + n + "</td>"; page += "<td style=\"padding: 1px 1px 1px 5px;\">" + NodeDefs[WeatherFlowNS.NS.NodeList[n]] + "</td></tr>\n"; } catch { WFLogging.Error("Missing definition for node type " + WeatherFlowNS.NS.NodeList[n]); } } page += "</table>\n"; page += "</div>\n"; page += "</div>\n"; page += "</body> </html> \n"; return(page); }
private void ConfigPage(HttpListenerContext context) { string cfg_page; byte[] page; byte[] post = new byte[1024]; Thread do_something; //Console.WriteLine("content length = " + context.Request.ContentLength64.ToString()); if (context.Request.ContentLength64 > 0) { string[] list; string[] pair; int len = (int)context.Request.ContentLength64; bool initISY = false; bool saveCfg = false; context.Request.InputStream.Read(post, 0, len); string resp = Encoding.Default.GetString(post); resp = resp.Substring(0, len); //Console.WriteLine("Response = " + resp); cfg_file_status = ""; if (resp.Contains("stationcfg")) { station_form a_form = GetStationInfo(resp); switch (a_form.action) { case "Delete": WFLogging.Info("Delete station " + a_form.station_id.ToString()); WeatherFlowNS.NS.DeleteStation(a_form.station_id); saveCfg = true; break; case "++Add++": bool exists = false; WFLogging.Info("Add station " + a_form.station_id.ToString()); // Check if station already exists foreach (StationInfo s in WF_Config.WFStationInfo) { if (s.station_id == a_form.station_id) { cfg_file_status = "Station " + a_form.station_id.ToString() + " already exists."; exists = true; break; } } if (!exists) { saveCfg = AddStation(a_form); } break; case "Update": WFLogging.Info("Update station " + a_form.station_id.ToString()); saveCfg = AddStation(a_form); break; default: WFLogging.Warning("Unknown action [" + a_form.action + "]"); break; } } else if (resp.Contains("upload")) { // Upload profile files TODO: Should this be done in a thread? do_something = new Thread(WeatherFlowNS.NS.UpdateProfileFiles); do_something.IsBackground = true; do_something.Start(); } else { list = resp.Split('&'); foreach (string item in list) { int v = 0; pair = item.Split('='); pair[1] = HttpUtility.UrlDecode(pair[1]); switch (pair[0]) { case "sAddress": if (pair[1] != WF_Config.ISY) { WF_Config.ISY = pair[1]; initISY = true; saveCfg = true; } break; case "sUsername": if (pair[1] != WF_Config.Username) { WF_Config.Username = pair[1]; initISY = true; saveCfg = true; } break; case "sPassword": if (pair[1] != WF_Config.Password) { WF_Config.Password = pair[1]; initISY = true; saveCfg = true; } break; case "sProfile": int.TryParse(pair[1], out v); if (v != WF_Config.Profile) { WF_Config.Profile = v; initISY = true; saveCfg = true; } break; case "webPort": int.TryParse(pair[1], out v); if (v != WF_Config.Port) { WF_Config.Port = v; saveCfg = true; } break; case "sSI": bool imperial = (pair[1] == "1"); if (imperial != WF_Config.SI) { WF_Config.SI = (pair[1] == "1"); saveCfg = true; } break; case "sHub": bool hub = (pair[1] == "1"); if (hub != WF_Config.Hub) { WF_Config.Hub = (pair[1] == "1"); saveCfg = true; } break; case "sDevice": bool device = (pair[1] == "1"); if (device != WF_Config.Device) { WF_Config.Device = (pair[1] == "1"); saveCfg = true; } break; case "sUnits": int.TryParse(pair[1], out v); if (v != WF_Config.Units) { WF_Config.Units = v; saveCfg = true; } break; case "sLogLevel": int.TryParse(pair[1], out v); if (v != WF_Config.LogLevel) { WF_Config.LogLevel = v; WFLogging.Level = (LOG_LEVELS)v; saveCfg = true; } break; case "serverctl": if (pair[1].Contains("Restart")) { WeatherFlowNS.NS.udp_client.Start(); WeatherFlowNS.NS.heartbeat.Start(); cfg_file_status = "Server Started"; Thread.Sleep(400); } else if (pair[1].Contains("Pause")) { WeatherFlowNS.NS.heartbeat.Stop(); WeatherFlowNS.NS.udp_client.Stop(); cfg_file_status = "Server Paused"; } break; case "websocket": if (pair[1].Contains("Start")) { try { WeatherFlowNS.NS.StartWebSocket(); } catch (Exception ex) { WFLogging.Error("Starting websocket client failed: " + ex.Message); } cfg_file_status = "Websocket Client Started"; Thread.Sleep(400); } else if (pair[1].Contains("Stop")) { WeatherFlowNS.NS.wsi.Stop(); cfg_file_status = "Websocket Client Stopped"; Thread.Sleep(800); } break; default: break; } } } if (saveCfg) { cfg_file_status += WeatherFlowNS.SaveConfiguration(); } if (initISY) { WeatherFlowNS.NS.InitializeISY(); } } try { cfg_page = MakeConfigPage(); } catch (Exception ex) { WFLogging.Error("Failed to make configuration web page."); WFLogging.Error(ex.Message); context.Response.Close(); return; } // How can we substitute values into the page? May need to dynamically // generate the page instead of storing it as a resource. That would // be a bit of a pain. page = ASCIIEncoding.ASCII.GetBytes(cfg_page); context.Response.ContentType = "text/html"; context.Response.ContentLength64 = page.Length; context.Response.AddHeader("Date", DateTime.Now.ToString("r")); context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.OutputStream.Write(page, 0, page.Length); context.Response.OutputStream.Flush(); context.Response.OutputStream.Close(); context.Response.Close(); }
private void Process(HttpListenerContext context) { string filename = context.Request.Url.AbsolutePath; //Console.WriteLine(" request = " + filename); if (filename.Contains("config") || filename == "/") { // Handle configuration ConfigPage(context); return; } if (filename.Contains("/log")) { WFWebLog.LogPage(context); return; } if (filename == "/wflog") { WFWebLog.LogText(context); return; } if (filename == "/nodeinfo") { WFNodeInfo.NodeDocPage(context); return; } // WeatherFlow/install - install // Weatherflow/nodes/<address>/query - Query the node and report status // WeatherFlow/nodes/<address>/status - report current status // WeatherFlow/add/nodes - Add all nodes // These could have a ?requestid=<requestid> at the end // that means we need to send a message after completing the task // WeatherFlow/nodes/<address>/report/add // WeatherFlow/nodes/<address>/report/remove // WeatherFlow/nodes/<address>/report/rename // WeatherFlow/nodes/<address>/report/enable // WeatherFlow/nodes/<address>/report/disable // TODO: Parse out the request id if present if (filename.Contains("install")) { WFLogging.Info("Recieved request to install the profile files."); } else if (filename.Contains("query")) { string[] parts; parts = filename.Split('/'); WFLogging.Info("Query node " + parts[3]); } else if (filename.Contains("status")) { string[] parts; parts = filename.Split('/'); WFLogging.Info("Get status of node " + parts[3]); NodeStatus(); } else if (filename.Contains("add")) { WFLogging.Info("Add our node. How is this different from report/Add?"); AddNodes(); // the report API is not yet implemented on the ISY so we'll // never get anything of these until it is. } else if (filename.Contains("report/add")) { WFLogging.Info("Report that a node was added?"); } else if (filename.Contains("report/rename")) { } else if (filename.Contains("report/remove")) { } else if (filename.Contains("report/enable")) { } else if (filename.Contains("report/disable")) { } else if (filename.Contains("favicon.ico")) { } else { WFLogging.Error("Unknown Request: " + filename); } context.Response.ContentType = "text/plain"; context.Response.ContentLength64 = 0; context.Response.AddHeader("Date", DateTime.Now.ToString("r")); context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.OutputStream.Flush(); context.Response.OutputStream.Close(); context.Response.Close(); }
internal void WSObservations(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); ObsData obs; double elevation = 0; try { try { obs = serializer.Deserialize <ObsData>(json); } catch (Exception ex) { WFLogging.Error("Deserialization failed for WebSocket data: " + ex.Message); WFLogging.Error(json); return; } if (json.Contains("obs_sky")) { //SkyObj = new SkyData(); // The first websocket packet seems to be cached data and it // doesn't include some things like the device serial number. // Without the serial number, we can't really process it if (obs.source == "cache") { return; } SkyObj.device_id = obs.device_id; SkyObj.firmware_revision = obs.firmware_revision; SkyObj.hub_sn = obs.hub_sn; SkyObj.obs = obs.obs; SkyObj.serial_number = obs.serial_number; SkyObj.type = obs.type; SkyObj.valid = true; WFNodeServer.SkyEventArgs evnt = new SkyEventArgs(SkyObj); evnt.SetDaily = CalcDailyPrecipitation(); evnt.Raw = json; // This fails the first time, why? WeatherFlowNS.NS.RaiseSkyEvent(this, evnt); WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)SkyObj.obs[0][0], SkyObj.serial_number, DataType.SKY)); } else if (json.Contains("obs_air")) { //AirObj = new AirData(); if (obs.source == "cache") { return; } AirObj.device_id = obs.device_id; AirObj.firmware_revision = obs.firmware_revision; AirObj.hub_sn = obs.hub_sn; AirObj.obs = obs.obs; AirObj.serial_number = obs.serial_number; AirObj.type = obs.type; AirObj.valid = true; // Look up elevation StationInfo si = wf_station.FindStationAir(AirObj.serial_number); if (si != null) { elevation = si.elevation; } AirEventArgs evnt = new AirEventArgs(AirObj); evnt.SetDewpoint = 0; evnt.SetApparentTemp = 0; evnt.SetTrend = 1; evnt.SetSeaLevel = SeaLevelPressure(AirObj.obs[0][(int)AirIndex.PRESSURE].GetValueOrDefault(), elevation); evnt.Raw = json; if (SkyObj.valid) { try { evnt.SetDewpoint = CalcDewPoint(); evnt.SetApparentTemp = FeelsLike(AirObj.obs[0][(int)AirIndex.TEMPURATURE].GetValueOrDefault(), AirObj.obs[0][(int)AirIndex.HUMIDITY].GetValueOrDefault(), SkyObj.obs[0][(int)SkyIndex.WIND_SPEED].GetValueOrDefault()); // Trend is -1, 0, 1 while event wants 0, 1, 2 evnt.SetTrend = PressureTrend() + 1; // Heat index & Windchill ?? } catch { } } else { } WeatherFlowNS.NS.RaiseAirEvent(this, evnt); WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)AirObj.obs[0][0], AirObj.serial_number, DataType.AIR)); } } catch (Exception ex) { WFLogging.Error("Failed to process websocket observation data: " + ex.Message); return; } }
private void AirObservations(string json) { JavaScriptSerializer serializer = new JavaScriptSerializer(); // obs[0][0] = time (seconds) // obs[0][1] = station pressure (MB) // obs[0][2] = air temp (c) // obs[0][3] = humidity (%) // obs[0][4] = lightning count // obs[0][5] = avg lightning dist (km) // obs[0][6] = battery // obs[0][7] = interval (minutes) try { double elevation = 0; AirObj = serializer.Deserialize <AirData>(json); AirObj.valid = true; // Look up elevation StationInfo si = wf_station.FindStationAir(AirObj.serial_number); if (si != null) { elevation = si.elevation; } // Do we just want to raise an event with the data object? AirEventArgs evnt = new AirEventArgs(AirObj); evnt.SetDewpoint = 0; evnt.SetApparentTemp = 0; evnt.SetTrend = 1; evnt.SetSeaLevel = SeaLevelPressure(AirObj.obs[0][(int)AirIndex.PRESSURE].GetValueOrDefault(), elevation); evnt.Raw = json; if (SkyObj.valid) { try { evnt.SetDewpoint = CalcDewPoint(); evnt.SetApparentTemp = FeelsLike(AirObj.obs[0][(int)AirIndex.TEMPURATURE].GetValueOrDefault(), AirObj.obs[0][(int)AirIndex.HUMIDITY].GetValueOrDefault(), SkyObj.obs[0][(int)SkyIndex.WIND_SPEED].GetValueOrDefault()); // Trend is -1, 0, 1 while event wants 0, 1, 2 evnt.SetTrend = PressureTrend() + 1; // Heat index & Windchill ?? } catch { } } else { } try { WeatherFlowNS.NS.RaiseAirEvent(this, evnt); } catch (Exception ex) { WFLogging.Warning("Failed to process Air event. " + ex.Message); } WeatherFlowNS.NS.RaiseUpdateEvent(this, new UpdateEventArgs((int)AirObj.obs[0][0].GetValueOrDefault(), AirObj.serial_number, DataType.AIR)); } catch (Exception ex) { WFLogging.Error("Deserialization failed for air data: " + ex.Message); WFLogging.Verbose(json); } }
internal void WeatherFlowThread() { Socket s; IPEndPoint groupEP; EndPoint remoteEP; string json; int len; using (s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); groupEP = new IPEndPoint(IPAddress.Any, Port); s.Bind(groupEP); remoteEP = (EndPoint)groupEP; Active = true; while (Active) { if (s.Available == 0) { Thread.Sleep(200); continue; } try { // Listen for UDP packets //byte[] bytes = listener.Receive(ref groupEP); byte[] bytes = new Byte[1500]; len = s.ReceiveFrom(bytes, ref remoteEP); if (len > 0) { json = Encoding.ASCII.GetString(bytes, 0, len); if (json.Contains("obs_air")) { //Console.WriteLine(json); AirObservations(json); } else if (json.Contains("obs_sky")) { SkyObservations(json); } else if (json.Contains("rapid_wind")) { RapidWindEvt(json); } else if (json.Contains("evt_strike")) { LigtningStrikeEvt(json); } else if (json.Contains("evt_precip")) { PrecipitationEvt(json); } else if (json.Contains("device_status")) { DeviceStatus(json); } else if (json.Contains("hub_status")) { HubStatus(json); } else if (json.Contains("obs_tower")) { // Skip } else { // Unknown type of packet } } } catch (Exception ex) { WFLogging.Error("UDP Listener failed: " + ex.Message); } } threadDone.Set(); } }