From b4f4e5f79517195490e98903b2eb187b0f3dc618 Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Sun, 20 Nov 2016 21:31:27 -0500 Subject: [PATCH] 1.0.6 - Added thermostat status and auxiliary temp to web service API --- HAILogger/CoreServer.cs | 103 ++++++++++++++++++--------- HAILogger/HAIService.cs | 72 +++++++++++++------ HAILogger/Helper.cs | 20 ++++-- HAILogger/IHAIService.cs | 8 +++ HAILogger/Properties/AssemblyInfo.cs | 4 +- HAILogger/ThermostatContract.cs | 6 ++ HAILogger/ZoneContract.cs | 9 ++- README.md | 5 +- 8 files changed, 164 insertions(+), 63 deletions(-) diff --git a/HAILogger/CoreServer.cs b/HAILogger/CoreServer.cs index 85a54ce..2fdaf1a 100644 --- a/HAILogger/CoreServer.cs +++ b/HAILogger/CoreServer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Data; using System.Data.Odbc; using System.IO; +using System.Reflection; using System.Text; using System.Threading; @@ -47,11 +48,12 @@ namespace HAILogger Global.event_source = "HAI Logger"; if (string.IsNullOrEmpty(Global.dir_config)) - Global.dir_config = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); + Global.dir_config = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); Settings.LoadSettings(); - Event.WriteInfo("CoreServer", "Starting up server"); + Event.WriteInfo("CoreServer", "Starting up server " + + Assembly.GetExecutingAssembly().GetName().Version.ToString()); tstat_timer.Elapsed += tstat_timer_Elapsed; tstat_timer.AutoReset = false; @@ -74,7 +76,7 @@ namespace HAILogger WebService web = new WebService(HAC); - if (Global.webapi_enabled) + if (Global.webapi_enabled) web.Start(); Connect(); @@ -157,7 +159,7 @@ namespace HAILogger Disconnect(); HAC = null; - if(Global.mysql_logging) + if (Global.mysql_logging) DBClose(); terminate = true; @@ -270,7 +272,7 @@ namespace HAILogger Event.WriteInfo("CoreServer", "CONNECTION STATUS: Disconnected"); break; case enuOmniLinkCommStatus.InterruptedFunctionCall: - if(!quitting) + if (!quitting) Event.WriteError("CoreServer", "CONNECTION STATUS: Interrupted Function Call"); break; case enuOmniLinkCommStatus.PermissionDenied: @@ -478,7 +480,7 @@ namespace HAILogger switch ((enuOmniLink2MessageType)B[2]) { case enuOmniLink2MessageType.EOD: - + break; case enuOmniLink2MessageType.Properties: @@ -488,9 +490,13 @@ namespace HAILogger { case enuObjectType.Area: HAC.Areas.CopyProperties(MSG); - break; + break; case enuObjectType.Zone: HAC.Zones.CopyProperties(MSG); + + if (HAC.Zones[MSG.ObjectNumber].IsTemperatureZone()) + HAC.Connection.Send(new clsOL2MsgRequestExtendedStatus(HAC.Connection, enuObjectType.Auxillary, MSG.ObjectNumber, MSG.ObjectNumber), HandleRequestAuxillaryStatus); + break; case enuObjectType.Thermostat: HAC.Thermostats.CopyProperties(MSG); @@ -606,7 +612,7 @@ namespace HAILogger break; } - if(Global.verbose_unhandled && !handled) + if (Global.verbose_unhandled && !handled) Event.WriteVerbose("CoreServer", "Unhandled notification: " + ((enuOmniLink2MessageType)B[2]).ToString()); } @@ -625,7 +631,7 @@ namespace HAILogger { type = enuEventType.USER_MACRO_BUTTON; value = ((int)MSG.SystemEvent).ToString() + " " + HAC.Buttons[MSG.SystemEvent].Name; - + LogEventStatus(type, value, alert); } else if (MSG.SystemEvent >= 768 && MSG.SystemEvent <= 771) @@ -753,6 +759,19 @@ namespace HAILogger MSG.ObjectNumber(i), HAC.Areas[MSG.ObjectNumber(i)]))); } break; + case enuObjectType.Auxillary: + for (byte i = 0; i < MSG.AuxStatusCount(); i++) + { + HAC.Zones[MSG.ObjectNumber(i)].CopyAuxExtendedStatus(MSG, i); + LogZoneStatus(MSG.ObjectNumber(i)); + + if (HAC.Zones[MSG.ObjectNumber(i)].IsTemperatureZone()) + { + WebNotification.Send("temp", Helper.Serialize(Helper.ConvertZone( + MSG.ObjectNumber(i), HAC.Zones[MSG.ObjectNumber(i)]))); + } + } + break; case enuObjectType.Zone: for (byte i = 0; i < MSG.ZoneStatusCount(); i++) { @@ -837,6 +856,19 @@ namespace HAILogger } #endregion + private void HandleRequestAuxillaryStatus(clsOmniLinkMessageQueueItem M, byte[] B, bool Timeout) + { + if (Timeout) + return; + + clsOL2MsgExtendedStatus MSG = new clsOL2MsgExtendedStatus(HAC.Connection, B); + + for (byte i = 0; i < MSG.AuxStatusCount(); i++) + { + HAC.Zones[MSG.ObjectNumber(i)].CopyAuxExtendedStatus(MSG, i); + } + } + #region Thermostats static double ThermostatTimerInterval() { @@ -860,12 +892,12 @@ namespace HAILogger { HAC.Connection.Send(new clsOL2MsgRequestExtendedStatus(HAC.Connection, enuObjectType.Thermostat, tstat.Key, tstat.Key), HandleRequestThermostatStatus); - if(Global.verbose_thermostat_timer) + if (Global.verbose_thermostat_timer) Event.WriteVerbose("ThermostatTimer", "Polling status for " + HAC.Thermostats[tstat.Key].Name); } // Log every minute if update within 5 minutes and connected - if (RoundToMinute(tstat.Value).AddMinutes(5) > RoundToMinute(DateTime.Now) && + if (RoundToMinute(tstat.Value).AddMinutes(5) > RoundToMinute(DateTime.Now) && (HAC.Connection.ConnectionState == enuOmniLinkConnectionState.Online || HAC.Connection.ConnectionState == enuOmniLinkConnectionState.OnlineSecure)) { @@ -918,7 +950,7 @@ namespace HAILogger private void tsync_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (tsync_check.AddMinutes(Global.hai_time_interval) < DateTime.Now) - HAC.Connection.Send(new clsOL2MsgRequestSystemStatus(HAC.Connection), HandleRequestSystemStatus); + HAC.Connection.Send(new clsOL2MsgRequestSystemStatus(HAC.Connection), HandleRequestSystemStatus); tsync_timer.Interval = TimeTimerInterval(); tsync_timer.Start(); @@ -951,7 +983,7 @@ namespace HAILogger (byte)now.Hour, (byte)now.Minute, (byte)(now.IsDaylightSavingTime() ? 1 : 0)), HandleSetTime); return; - } + } double adj = (DateTime.Now - time).Duration().TotalSeconds; @@ -960,7 +992,7 @@ namespace HAILogger Event.WriteWarn("TimeSyncTimer", "Controller time " + time.ToString("MM/dd/yyyy HH:mm:ss") + " out of sync by " + adj + " seconds", true); DateTime now = DateTime.Now; - HAC.Connection.Send(new clsOL2MsgSetTime(HAC.Connection, (byte)(now.Year % 100), (byte)now.Month, (byte)now.Day, (byte)now.DayOfWeek, + HAC.Connection.Send(new clsOL2MsgSetTime(HAC.Connection, (byte)(now.Year % 100), (byte)now.Month, (byte)now.Day, (byte)now.DayOfWeek, (byte)now.Hour, (byte)now.Minute, (byte)(now.IsDaylightSavingTime() ? 1 : 0)), HandleSetTime); } } @@ -1001,10 +1033,10 @@ namespace HAILogger Event.WriteAlarm("AreaStatus", "FIRE " + unit.Name + " " + unit.AreaFireAlarmText); Prowl.Notify("ALARM", "FIRE " + unit.Name + " " + unit.AreaFireAlarmText, ProwlPriority.Emergency); - if(!alarms.Contains("FIRE" + id)) + if (!alarms.Contains("FIRE" + id)) alarms.Add("FIRE" + id); } - else if(alarms.Contains("FIRE" + id)) + else if (alarms.Contains("FIRE" + id)) { Event.WriteAlarm("AreaStatus", "CLEARED - FIRE " + unit.Name + " " + unit.AreaFireAlarmText); Prowl.Notify("ALARM CLEARED", "FIRE " + unit.Name + " " + unit.AreaFireAlarmText, ProwlPriority.High); @@ -1076,10 +1108,10 @@ namespace HAILogger unit.AreaFireAlarmText + "','" + unit.AreaBurglaryAlarmText + "','" + unit.AreaAuxAlarmText + "','" + unit.AreaDuressAlarmText + "','" + status + "')"); - if(Global.verbose_area) + if (Global.verbose_area) Event.WriteVerbose("AreaStatus", id + " " + unit.Name + ", Status: " + status); - if(unit.LastMode != unit.AreaMode) + if (unit.LastMode != unit.AreaMode) Prowl.Notify("Security", unit.Name + " " + unit.ModeText()); } @@ -1090,9 +1122,14 @@ namespace HAILogger DBQueue(@" INSERT INTO log_zones (timestamp, id, name, status) VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + id.ToString() + "','" + unit.Name + "','" + unit.StatusText() + "')"); - - if(Global.verbose_zone) - Event.WriteVerbose("ZoneStatus", id + " " + unit.Name + ", Status: " + unit.StatusText()); + + if (Global.verbose_zone) + { + if (unit.IsTemperatureZone()) + Event.WriteVerbose("ZoneStatus", id + " " + unit.Name + ", Temp: " + unit.TempText()); + else + Event.WriteVerbose("ZoneStatus", id + " " + unit.Name + ", Status: " + unit.StatusText()); + } } private void LogThermostatStatus(ushort id) @@ -1118,13 +1155,13 @@ namespace HAILogger humidity + "','" + humidify + "','" + dehumidify + "','" + unit.ModeText() + "','" + unit.FanModeText() + "','" + unit.HoldStatusText() + "')"); - if(Global.verbose_thermostat) + if (Global.verbose_thermostat) Event.WriteVerbose("ThermostatStatus", id + " " + unit.Name + - ", Status: " + unit.TempText() + " " + unit.HorC_StatusText() + + ", Status: " + unit.TempText() + " " + unit.HorC_StatusText() + ", Heat: " + unit.HeatSetpointText() + - ", Cool: " + unit.CoolSetpointText() + - ", Mode: " + unit.ModeText() + - ", Fan: " + unit.FanModeText() + + ", Cool: " + unit.CoolSetpointText() + + ", Mode: " + unit.ModeText() + + ", Fan: " + unit.FanModeText() + ", Hold: " + unit.HoldStatusText()); } @@ -1145,8 +1182,8 @@ namespace HAILogger VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + id.ToString() + "','" + unit.Name + "','" + status + "','" + unit.Status + "','" + unit.StatusTime + "')"); - if(Global.verbose_unit) - Event.WriteVerbose("UnitStatus", id + " " + unit.Name + ", Status: " + status); + if (Global.verbose_unit) + Event.WriteVerbose("UnitStatus", id + " " + unit.Name + ", Status: " + status); } private void LogMessageStatus(ushort id) @@ -1157,10 +1194,10 @@ namespace HAILogger INSERT INTO log_messages (timestamp, id, name, status) VALUES ('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + id.ToString() + "','" + unit.Name + "','" + unit.StatusText() + "')"); - if(Global.verbose_message) + if (Global.verbose_message) Event.WriteVerbose("MessageStatus", unit.Name + ", " + unit.StatusText()); - if(Global.prowl_messages) + if (Global.prowl_messages) Prowl.Notify("Message", id + " " + unit.Name + ", " + unit.StatusText()); } @@ -1188,7 +1225,7 @@ namespace HAILogger public void DBClose() { - if(mysql_conn.State != ConnectionState.Closed) + if (mysql_conn.State != ConnectionState.Closed) mysql_conn.Close(); } @@ -1197,7 +1234,7 @@ namespace HAILogger if (!Global.mysql_logging) return; - lock(mysql_lock) + lock (mysql_lock) mysql_queue.Enqueue(query); } @@ -1211,4 +1248,4 @@ namespace HAILogger } #endregion } -} +} \ No newline at end of file diff --git a/HAILogger/HAIService.cs b/HAILogger/HAIService.cs index 130f2b0..6c7a7ec 100644 --- a/HAILogger/HAIService.cs +++ b/HAILogger/HAIService.cs @@ -119,35 +119,57 @@ namespace HAILogger return names; } + public List ListZonesTemp() + { + Event.WriteVerbose("WebService", "ListZonesTemp"); + + List names = new List(); + for (ushort i = 1; i < WebService.HAC.Zones.Count; i++) + { + clsZone zone = WebService.HAC.Zones[i]; + + if (zone.IsTemperatureZone() && zone.DefaultProperties == false) + names.Add(new NameContract() { id = i, name = zone.Name }); + } + return names; + } + public ZoneContract GetZone(ushort id) { Event.WriteVerbose("WebService", "GetZone: " + id); WebOperationContext ctx = WebOperationContext.Current; - switch (WebService.HAC.Zones[id].ZoneType) + if (WebService.HAC.Zones[id].IsTemperatureZone()) { - case enuZoneType.EntryExit: - case enuZoneType.X2EntryDelay: - case enuZoneType.X4EntryDelay: - case enuZoneType.Perimeter: - ctx.OutgoingResponse.Headers.Add("type", "contact"); - break; - case enuZoneType.AwayInt: - ctx.OutgoingResponse.Headers.Add("type", "motion"); - break; - case enuZoneType.Water: - ctx.OutgoingResponse.Headers.Add("type", "water"); - break; - case enuZoneType.Fire: - ctx.OutgoingResponse.Headers.Add("type", "smoke"); - break; - case enuZoneType.Gas: - ctx.OutgoingResponse.Headers.Add("type", "co"); - break; - default: - ctx.OutgoingResponse.Headers.Add("type", "unknown"); - break; + ctx.OutgoingResponse.Headers.Add("type", "temp"); + } + else + { + switch (WebService.HAC.Zones[id].ZoneType) + { + case enuZoneType.EntryExit: + case enuZoneType.X2EntryDelay: + case enuZoneType.X4EntryDelay: + case enuZoneType.Perimeter: + ctx.OutgoingResponse.Headers.Add("type", "contact"); + break; + case enuZoneType.AwayInt: + ctx.OutgoingResponse.Headers.Add("type", "motion"); + break; + case enuZoneType.Water: + ctx.OutgoingResponse.Headers.Add("type", "water"); + break; + case enuZoneType.Fire: + ctx.OutgoingResponse.Headers.Add("type", "smoke"); + break; + case enuZoneType.Gas: + ctx.OutgoingResponse.Headers.Add("type", "co"); + break; + default: + ctx.OutgoingResponse.Headers.Add("type", "unknown"); + break; + } } clsZone unit = WebService.HAC.Zones[id]; @@ -251,6 +273,12 @@ namespace HAILogger WebService.HAC.SendCommand(enuUnitCommand.Fan, BitConverter.GetBytes(unit.value)[0], unit.id); } + public void SetThermostatHold(CommandContract unit) + { + Event.WriteVerbose("WebService", "SetThermostatHold: " + unit.id + " to " + unit.value); + WebService.HAC.SendCommand(enuUnitCommand.Hold, BitConverter.GetBytes(unit.value)[0], unit.id); + } + public List ListButtons() { Event.WriteVerbose("WebService", "ListButtons"); diff --git a/HAILogger/Helper.cs b/HAILogger/Helper.cs index 5da7f7f..4188cf9 100644 --- a/HAILogger/Helper.cs +++ b/HAILogger/Helper.cs @@ -21,9 +21,7 @@ namespace HAILogger string mode = area.ModeText(); - if (mode.Contains("OFF")) - ret.mode = "OFF"; - else if (mode.Contains("DAY")) + if (mode.Contains("DAY")) ret.mode = "DAY"; else if (mode.Contains("NIGHT")) ret.mode = "NIGHT"; @@ -31,6 +29,8 @@ namespace HAILogger ret.mode = "AWAY"; else if (mode.Contains("VACATION")) ret.mode = "VACATION"; + else + ret.mode = "OFF"; return ret; } @@ -40,8 +40,10 @@ namespace HAILogger ZoneContract ret = new ZoneContract(); ret.id = id; + ret.zonetype = zone.ZoneType; ret.name = zone.Name; ret.status = zone.StatusText(); + ret.temp = zone.TempText(); return ret; } @@ -82,7 +84,17 @@ namespace HAILogger ret.heatsetpoint = heat; ret.coolsetpoint = cool; ret.mode = unit.Mode; - ret.fanmode = unit.FanMode; + ret.fanmode = unit.FanMode; + ret.hold = unit.HoldStatus; + + string status = unit.HorC_StatusText(); + + if (status.Contains("COOLING")) + ret.status = "COOLING"; + else if (status.Contains("HEATING")) + ret.status = "HEATING"; + else + ret.status = "OFF"; return ret; } diff --git a/HAILogger/IHAIService.cs b/HAILogger/IHAIService.cs index 8795cf8..05d3eeb 100644 --- a/HAILogger/IHAIService.cs +++ b/HAILogger/IHAIService.cs @@ -39,6 +39,10 @@ namespace HAILogger [WebGet(ResponseFormat = WebMessageFormat.Json)] List ListZonesCO(); + [OperationContract] + [WebGet(ResponseFormat = WebMessageFormat.Json)] + List ListZonesTemp(); + [OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json)] ZoneContract GetZone(ushort id); @@ -83,6 +87,10 @@ namespace HAILogger [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] void SetThermostatFanMode(CommandContract unit); + [OperationContract] + [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] + void SetThermostatHold(CommandContract unit); + [OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json)] List ListButtons(); diff --git a/HAILogger/Properties/AssemblyInfo.cs b/HAILogger/Properties/AssemblyInfo.cs index fb4e7c2..0493b4c 100644 --- a/HAILogger/Properties/AssemblyInfo.cs +++ b/HAILogger/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.5.0")] -[assembly: AssemblyFileVersion("1.0.5.0")] +[assembly: AssemblyVersion("1.0.6.0")] +[assembly: AssemblyFileVersion("1.0.6.0")] diff --git a/HAILogger/ThermostatContract.cs b/HAILogger/ThermostatContract.cs index d96fad7..1691225 100644 --- a/HAILogger/ThermostatContract.cs +++ b/HAILogger/ThermostatContract.cs @@ -29,5 +29,11 @@ namespace HAILogger [DataMember] public enuThermostatFanMode fanmode { get; set; } + + [DataMember] + public enuThermostatHoldMode hold { get; set; } + + [DataMember] + public string status { get; set; } } } diff --git a/HAILogger/ZoneContract.cs b/HAILogger/ZoneContract.cs index 3c0d28d..55609e9 100644 --- a/HAILogger/ZoneContract.cs +++ b/HAILogger/ZoneContract.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using HAI_Shared; +using System.Runtime.Serialization; namespace HAILogger { @@ -8,10 +9,16 @@ namespace HAILogger [DataMember] public ushort id { get; set; } + [DataMember] + public enuZoneType zonetype { get; set; } + [DataMember] public string name { get; set; } [DataMember] public string status { get; set; } + + [DataMember] + public string temp { get; set; } } } diff --git a/README.md b/README.md index 84a86a9..556083f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Provides logging and web service API for HAI/Leviton OmniPro II controllers ##Download -You can download the [binary here](http://www.excalibur-partners.com/downloads/HAILogger_1_0_5.zip) +You can download the [binary here](http://www.excalibur-partners.com/downloads/HAILogger_1_0_6.zip) ##Requirements - .NET Framework 4.0 @@ -58,6 +58,9 @@ To test the API you can use your browser to view a page or PowerShell (see below - Invoke-WebRequest -Uri "http://localhost:8000/SetUnit" -Method POST -ContentType "application/json" -Body (convertto-json -InputObject @{"id"=1;"value"=100}) -UseBasicParsing ##Change Log +Version 1.0.6 - 2016-11-20 +- Added thermostat status and auxiliary temp to web service API + Version 1.0.5 - 2016-11-15 - Added web service API for Samsung SmartThings integration