Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 25 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This project is an intelligent home thermostat that tries to improve on products like the nest learning thermostat. Those products require you to keep setting them manually and they eventually figure out your pattern. If your needs or schedules change, they have to gradually guess a new pattern as you manually fix them each day. Eventually this could grow into an entire smart home solution.
It's name is Halcyon, because the initial idea was to harness the overwhelming power of Cortana, who already knows all about my likes and schedule, to control things beyond my phone and laptop. Halcyon was the first class of ship she was the artificial intelligence for, so what better name for the system that can literally be her home too.
This thermostat is integrated into your digital calendar. You can configure a handful of simple settings (such as regular work schedule which may not appear in your calendar) and desired temperatures. Then the Halcyon system will intelligently read your calendar to determine when you are going to be home and adjust accordingly. You can have multiple people in the house all feeding calendar data to Halcyon and it will intelligently make choices based on what's going on in the real world.
Have a party scheduled? Halcyon will see in your calendar that 20 people are coming over and automatically drop a few degrees to keep things comfortable. Drinks after work this evening? Halcyon knows you aren't coming home right away and doesn't blast the AC until it needs to. Stay home from work today? Just tap the smartphone app to override Halcyon for the day and keep yourself just right. Doing this will not mess up Halcyon's decisions or start it trying to guess a new pattern. Sometimes, you just need to be in control.
The smartphone companion app is not yet made so all settings are currently hardcoded to their variables. Eventually this can be fully integrated with Cortana and your live calendar feed, allowing you to control it from your smartphone, website, or your Xbox One.
Also, being very novice (this is my first ever project like this), I don't know how to directly link to the calendar on my Microsoft Account, so I've shared the calendar XML for now via the link option. I'm still tweaking the code to parse that and it's not 100% ready for use yet. So look at this as a proof-of-concept and not something that's release ready.
I have tested reading temperature values and triggering the relays already, and the logic is pretty straight forward. However, in my latest full test tonight the relays weren't triggering correctly. I know they're getting triggered because the onboard LEDs light - but the relays are literally just not switching. So we're very close but I have to turn this in by tonight so must draw a line in the sand.
Fear not though, for I will keep trying to finish this in the coming weeks - I just won't win anything from Microsoft or Hacster.io for it =(
Pictures below are my final test before it's due. Still not working completely but VERY close.
HalcyonHardware.vb
VBScriptImports Windows.Devices.Gpio
Imports Windows.Devices.Spi
Imports Windows.Devices.Enumeration
'This module is for any code that directly interacts with hardware
Module HalcyonHardware
Private Const RELAY_PIN_AC_NUM As Integer = 26 'Use GPIO number, not literal RPi pin number
Private Const RELAY_PIN_HEAT_NUM As Integer = 13 'Use GPIO number, not literal RPi pin number
Private RELAY_PIN_AC 'The pin object that we will use to send commands
Private RELAY_PIN_HEAT 'The pin object that we will use to send commands
Private SpiDisplay As SpiDevice
Private readBuffer As Byte() = New Byte(2) {} 'this Is defined to hold the output data from MCP3008
Private writeBuffer As Byte() = New Byte(2) {&H1, &H80, &H0} 'It Is SPI port serial input pin, And Is used To load channel configuration data into the device
Public Sub InitializeHardware()
InitializeRelayPins() 'This gets our GPIO pins ready to trigger the relays
InitializeSpiDisplay() 'This gets our SPI ready to read data from the MCP3008
End Sub
Public Sub ToggleRelays()
'This will turn devices plugged into the relays on or off
'low = off; high = on
If ac_ON Then RELAY_PIN_AC.write(GpioPinValue.High) Else RELAY_PIN_AC.write(GpioPinValue.Low)
If heat_ON Then RELAY_PIN_HEAT.write(GpioPinValue.High) Else RELAY_PIN_HEAT.write(GpioPinValue.Low)
End Sub
Private Function ByteToInt(data As Byte()) As Integer
'Comments assume readBuffer values of {00000000 , 00000010, 00110001} (0,2,49)
'imagine all three separate bytes of readbuffer just need to be combined into one long "string" to equal the REAL binary value. Since we know the MCP3008 outputs 10 bits,
' we can safely ignore the first byte entirely as it should always be all zeros
'So 10 bits = the entire 3rd byte (8 bits) and the least two positions of the second byte. First byte is unused.
Dim result As Integer = data(1) And &H3 'this combines the second byte with hexadecimal 3 (3 in decimal, 0011 in binary) which will result in major six positions returning zero and minor two positions keeping value
result <<= 8 'This shifts those bits to the left by 8 places leaving an empty byte of zeros (00000010 -> 00000010 00000000)
result += data(2) 'This fills that empty byte of zeros with the values from the third byte in the readBuffer, generating the stitched back together binary number that has 10 bits (plus an additional 4 major zero bits).
'(00000010 00000000 -> 00000010 00110001 = 561 in decimal)
Return result
End Function
Public Sub GetTemp()
Dim rawInt As Integer = 0
SpiDisplay.TransferFullDuplex(writeBuffer, readBuffer) 'writeBuffer programs the MCP3008 to return channel 0. That 10 bit data gets encoded into the readBuffer in the second two bytes
rawInt = ByteToInt(readBuffer) '10 bits from MCP3008 range 0 - 1023
TempAmbient_Celsius = Math.Round((((rawInt / 1024) * power_mVolts) - 500) / 10, precision)
End Sub
Private Sub InitializeRelayPins()
'This initializes the GPIO pins which will be used to activate the relays
Dim gpio = GpioController.GetDefault()
If gpio Is Nothing Then
Throw New Exception("GPIO not found")
End If
RELAY_PIN_AC = gpio.OpenPin(RELAY_PIN_AC_NUM)
If RELAY_PIN_AC Is Nothing Then
Throw New Exception("AC Relay Pin Error. GPIO=" & RELAY_PIN_AC_NUM)
End If
RELAY_PIN_AC.Write(GpioPinValue.Low) 'default to low
RELAY_PIN_AC.SetDriveMode(GpioPinDriveMode.Output)
RELAY_PIN_HEAT = gpio.OpenPin(RELAY_PIN_HEAT_NUM)
If RELAY_PIN_HEAT Is Nothing Then
Throw New Exception("Heat Relay Pin Error. GPIO=" & RELAY_PIN_HEAT_NUM)
End If
RELAY_PIN_HEAT.Write(GpioPinValue.Low) 'default to low = off
RELAY_PIN_HEAT.SetDriveMode(GpioPinDriveMode.Output)
End Sub
Private Async Sub InitializeSpiDisplay()
'This initializes the SPI interface used to communicate with MCP3008
Const SPI_CONTROLLER_NAME As String = "SPI0" 'For Raspberry Pi 2, use "SPI0"
Const SPI_CHIP_SELECT_LINE As Int16 = 0 'Line 0 maps To physical pin number 24 On the Rpi2
Try
Dim settings = New SpiConnectionSettings(SPI_CHIP_SELECT_LINE)
settings.ClockFrequency = 500000 'this should match your SPI Bus speed
settings.Mode = SpiMode.Mode0
Dim spiAqs As String = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME)
Dim deviceInfo = Await DeviceInformation.FindAllAsync(spiAqs)
SpiDisplay = Await SpiDevice.FromIdAsync(deviceInfo(0).Id, settings)
Catch ex As Exception
Throw New Exception("SPIO Error")
End Try
End Sub
End Module
HalcyonSettings.vb
VBScript'This module is for storing all the shared variables used in the program
'All settings are hardcoded and volatile for now. Later they will be configurable from your phone or web app
Module HalcyonSettings
Public UpdateTemp_Seconds As Integer = 1 'frequency of ambient temp updates
Public UpdateCal_Minutes As Integer = 60 'frequency of calendar sync updates
Public HomeIndicator As String() = {"26H", "Piscassic", "Gary"} 'if event location CONTAINS any of these strings, it's home
Public Const power_mVolts As Double = 5000 '3300 or 5000 depending on power supply to TMP36 and MCP3008
Public ac_ON As Boolean = False
Public heat_ON As Boolean = False
Public AllCalendars As List(Of HalcyonCalendar) 'This is a list of all calendars that should be considered, for the prototype it is just my own
Public CurrentEvent As HalcyonCalendarEvent 'This is the current HalcyonCalendarEvent that the temp setting is based on
Public Const precision As Integer = 2 'number of decimal places to always round the temp to
Private ActualTolerance_Celsius As Double = 1 'degrees Celsius of tolerance
Public Property TempTolerance_Celsius As Double 'interfaces with ActualTolerance_Celsius to ensure nothing below 1 or above 10
Get
Return ActualTolerance_Celsius
End Get
Set(value As Double)
If value < 1 Then value = 1
If value > 10 Then value = 10
ActualTolerance_Celsius = value
End Set
End Property
Public TempAmbient_Celsius As Double = 20 'this variable will hold the latest ambient temperature reading
Private ActualSet_Celsius As Double = 20 'degrees celsius of currently set temperature
Public Property TempSet_Celsius As Double 'interfaces with ActualSet_Celsius to enforce safety min/max
Get
Return ActualSet_Celsius
End Get
Set(value As Double)
If value < 1 Then value = 1 '1=33F
If value > 37 Then value = 37 '37=100F
ActualSet_Celsius = value
End Set
End Property
Public CalendarLinks As String() = {
"https://sharing.calendar.live.com/calendar/private/82856ffe-1447-4134-9164-2690d5cee38e/7661b193-8977-40ce-980f-1e20ff46d8ae/cid-60e973077a985b49/calendar.xml",
"https://sharing.calendar.live.com/calendar/private/eca6cd71-ae7c-4146-b7c1-a8f9d03de0d2/0ff376e0-b10d-4067-acde-71cd450283db/cid-60e973077a985b49/calendar.xml"}
Public TempEmpty_Min_Celsius As Double = 12 'min temp when house is empty in MinMax mode
Public TempEmpty_Max_Celsius As Double = 10 'max temp when house is empty in MinMax mode
Public TempOccupied_Day_Celsius As Double = 21 'Desired daytime temp
Public TempOccupied_Night_Celsius As Double = 16 'Desired nighttime temp
Public TempUnoccupied_Celsius As Double = 15 'Desired temp when house is empty in KeepTemp mode
Public UnoccupiedMode As DecisionMode = DecisionMode.MinMax 'How to behave when house is empty
Public PartyMode As PartyShift_Celsius = PartyShift_Celsius.Slight
Public PartyShift_Custom_Celsius As Double = 200
Public StartTime_Day As DateTime 'if current time is > StartTime_Day AND < StartTime_Night. It's Day
Public StartTime_Night As DateTime 'if current time is > StartTime_Night OR < StartTime_Day, It's Night
Public Enum DecisionMode
KeepTemp 'This will keep temp within tolerance to current setting
MinMax 'This will stop temp from falling outside of range, saves energy while unoccupied
DoNothing 'Will never activate heating or cooling, best energy savings while unoccupied
End Enum
Public Enum PartyShift_Celsius
'extra attendees at a home event will be cause the temp to be DECREASED by this/100 degrees
None = 000 'no change
Slight = 025 'about 0.5 F
Moderate = 050 'about 1.0 F
Aggressive = 075 'about 1.5 F
Custom = 999 'user defined
End Enum
Public ReadOnly Property TempAmbient_Farenheit As Double
'Returns the most recent ambient temperature reading converted to Farenheit
'If you're using a temperature sensor that reads in farenheit instead of celsius,
' make this Property Not ReadOnly And uncomment the Set method so you can write to it
Get
Return TempConverter(TempAmbient_Celsius, True)
End Get
'Set(value As Double)
' TempAmbient_Celsius = TempConverter(value, False)
'End Set
End Property
Public Property TempTolerance_Farenheit As Double
'Allows use of TempTolerance_Celsius using Farenheit
Get
Return TempConverter(TempTolerance_Celsius, True)
End Get
Set(value As Double)
TempTolerance_Celsius = TempConverter(value, False)
End Set
End Property
Public Property TempSet_Farenheit As Double
'Allows use of TempSet_Celsius using Farenheit
Get
Return TempConverter(TempSet_Celsius, True)
End Get
Set(value As Double)
TempSet_Celsius = TempConverter(value, False)
End Set
End Property
End Module
HalcyonSoftware.vb
VBScript'This module is for any code that does NOT interact with hardware
Module HalcyonSoftware
Private TimerTemp As DispatcherTimer 'Timer to update ambient temperature
Private TimerCal As DispatcherTimer 'Timer to sync calendar data
Public Sub InitializeSoftware()
InitializeCalendars()
UpdateCurrentEvent()
InitializeTimers()
End Sub
Public Sub stopTimers()
On Error Resume Next
TimerTemp.Stop()
TimerCal.Stop()
On Error GoTo 0
End Sub
Private Sub InitializeCalendars()
AllCalendars = New List(Of HalcyonCalendar)
For Each CalLink As String In CalendarLinks
Dim newCal As HalcyonCalendar = New HalcyonCalendar
newCal.str_Link = CalLink.Trim
newCal.Resync()
AllCalendars.Add(newCal)
Next
UpdateCurrentEvent()
End Sub
Public Function TempConverter(degrees As Double, CtoF As Boolean) As Double
If CtoF Then 'converting Celsius to Farenheit
Return Math.Round((degrees * 9 / 5) + 32, precision)
Else 'converting Farenheit to Celsius
Return Math.Round((degrees - 32) * 5 / 9, precision)
End If
End Function
Private Sub InitializeTimers()
'This initiates the timers used to get temp updates and calendar syncs
TimerTemp = New DispatcherTimer
TimerTemp.Interval = New TimeSpan(0, 0, 0, UpdateTemp_Seconds)
AddHandler TimerTemp.Tick, AddressOf TimerTemp_Tick
TimerTemp.Start()
TimerCal = New DispatcherTimer
TimerCal.Interval = New TimeSpan(0, 0, UpdateCal_Minutes, 0)
AddHandler TimerCal.Tick, AddressOf TimerCal_Tick
TimerCal.Start()
End Sub
Private Sub TimerTemp_Tick()
'If the current event has ended, then we need to update that.
If CurrentEvent IsNot Nothing AndAlso CurrentEvent.dt_EndTime < DateTime.Now Then
TimerTemp.Stop() 'Calendar update could take a moment, don't want the timer to keep firing while updating
UpdateCurrentEvent()
TimerTemp.Start()
End If
GetTemp()
MakeDecision()
End Sub
Private Sub MakeDecision()
'Should be called after getting an updated temp
'Will toggle heat and AC on or off based on current temp
Try
If CurrentEvent.AtHome Then
If DateTime.Now.TimeOfDay > StartTime_Day.TimeOfDay And DateTime.Now.TimeOfDay < StartTime_Night.TimeOfDay Then
TempSet_Celsius = TempOccupied_Day_Celsius
Else
TempSet_Celsius = TempOccupied_Night_Celsius
End If
'for party mode
'For example, slight = 025
'025 / 100 = 0.25
'if 3 people are attending, subtract one for the owner
'3 - 1 = 2 people extra, * .25 = 0.50 temp adjustment
TempSet_Celsius -= (PartyMode / 100) * Math.Max((CurrentEvent.int_Attending - 1), 0)
Else
Select Case UnoccupiedMode
Case DecisionMode.DoNothing
TempSet_Celsius = TempAmbient_Celsius 'if setting == current temp then it will do nothing
Case DecisionMode.KeepTemp
TempSet_Celsius = TempUnoccupied_Celsius 'keep the unoccupied setting
Case DecisionMode.MinMax
If TempAmbient_Celsius < TempEmpty_Min_Celsius Then
TempSet_Celsius = TempEmpty_Min_Celsius 'we are below min so set to min
ElseIf TempAmbient_Celsius > TempEmpty_Max_Celsius Then
TempSet_Celsius = TempEmpty_Max_Celsius 'we are above max so set to max
Else
TempSet_Celsius = TempAmbient_Celsius 'do nothing
End If
End Select
End If
Catch ex As Exception
'in case of error, just call me occupied
TempSet_Celsius = TempOccupied_Day_Celsius
Finally
'Simple if logic checks if we're more than tolerance away from setting
If TempAmbient_Celsius < (TempSet_Celsius - TempTolerance_Celsius) Then heat_ON = True Else heat_ON = False
If TempAmbient_Celsius > (TempSet_Celsius + TempTolerance_Celsius) Then ac_ON = True Else ac_ON = False
ToggleRelays() 'Turn on or off as needed
End Try
End Sub
Private Sub TimerCal_Tick()
'resync all the calendars
For Each oneCal As HalcyonCalendar In AllCalendars
oneCal.Resync()
Next
'update the current event based on new calendar data
UpdateCurrentEvent()
'make a decision in case current event changed
MakeDecision()
End Sub
Private Sub UpdateCurrentEvent()
Dim mainEvent As HalcyonCalendarEvent = New HalcyonCalendarEvent
mainEvent.dt_StartTime = DateTime.Now
mainEvent.dt_EndTime = DateTime.Now.AddYears(1).AddHours(1) 'puts the date 1 year and 1 day into the future. to be replaced by found events
mainEvent.AtHome = False
For Each oneCal As HalcyonCalendar In AllCalendars
For Each oneEvent As HalcyonCalendarEvent In oneCal.obj_Events
If oneEvent.dt_EndTime < DateTime.Now Or oneEvent.dt_StartTime < DateTime.Now Then
'Event is either in the past or the future, ignore it
Continue For
End If
'This event is currently happening! YAY!
If oneEvent.AtHome Then
If mainEvent.AtHome Then
'we are overwriting data because this is the first home event found
mainEvent.AtHome = True
mainEvent.int_Attending += oneEvent.int_Attending
'we keep the lowest end time so we can update the party temp when one overlapping event ends
If oneEvent.dt_EndTime < mainEvent.dt_EndTime Then mainEvent.dt_EndTime = oneEvent.dt_EndTime
End If
Else
'if multiple events overlap (for example, from different calendars), then the HOME event has priority
If mainEvent.AtHome = True Then
'a home event already has priority so skip this away event
Continue For
Else
mainEvent.str_Location = "Away"
mainEvent.AtHome = False
If oneEvent.dt_EndTime < mainEvent.dt_EndTime Then mainEvent.dt_EndTime = oneEvent.dt_EndTime
End If
End If
Next
Next
End Sub
Public Function IsAtHome(location_str As String) As Boolean
For Each s As String In HomeIndicator
If location_str.ToLower.Contains(s.ToLower) Then Return True
Next
Return False
End Function
End Module
HalcyonCalendar.vb
VBScriptImports System.Net.Http
Public Class HalcyonCalendar
Private xDoc As XDocument = Nothing
Public Property obj_Events As List(Of HalcyonCalendarEvent)
Public Property str_TimeZoneOffset As Integer
Public Property str_Link As String
Public Property dt_Updated As DateTime
Public Property str_Title As String
Public Property str_RawXML As String
'timezone?
Public Sub Resync()
Dim client As New HttpClient
Dim resp = client.GetAsync(str_Link)
str_RawXML = resp.Result.Content.ReadAsStringAsync.Result
Dim doc As New XDocument
doc = XDocument.Parse(str_RawXML)
Dim temporaryCal As HalcyonCalendar = Iterate_live(doc)
obj_Events = New List(Of HalcyonCalendarEvent)
obj_Events = temporaryCal.obj_Events
End Sub
Public Sub New()
Me.obj_Events = New List(Of HalcyonCalendarEvent)
Me.str_Title = "new calendar: title not set"
Me.str_Link = "new calendar: link not set"
End Sub
Public Sub AddEvent(obj As HalcyonCalendarEvent)
obj_Events.Add(obj)
End Sub
End Class
Public Class HalcyonCalendarEvent
Public Property dt_StartTime As DateTime
Public Property dt_EndTime As DateTime
Public Property str_Location As String
Public Property int_Attending As Integer
Public Property str_Title As String
Public Property str_RawContent As String
Public Property dt_Updated As DateTime
Public Property AtHome As Boolean
'timezone?
Public Sub New(StartTime As DateTime, EndTime As DateTime, Location As String, Optional Attending As Integer = 1, Optional AllDay As Boolean = False)
If AllDay Then
Me.dt_StartTime = New DateTime(StartTime.Year, StartTime.Month, StartTime.Day, 0, 0, 1)
Me.dt_EndTime = New DateTime(StartTime.Year, StartTime.Month, StartTime.Day, 23, 59, 59)
Else
Me.dt_StartTime = StartTime
Me.dt_EndTime = EndTime
End If
Me.str_Location = Location
Me.int_Attending = Attending
End Sub
Public Sub New()
End Sub
End Class
IterateXML.vb
VBScript'This module is responsible for iterating the calendar XML. It's complicated enough that I want it isolated for easier reading.
'In a future version, XML parsing will be replaced with actual Microsoft Account linking
Public Module IterateXML
Public Function Iterate_live(xDoc As XDocument) As HalcyonCalendar
'<?xml version="1.0" encoding="utf-8"?>
'feed
' title
' link
' updated
' entry
' title
' updated
' author
' email
' content
' timezone
Dim cal As New HalcyonCalendar
'pull raw info out of XML
For Each oneElement As XElement In xDoc.Root.Descendants()
Select Case oneElement.Name.LocalName
Case "link"
'we already know this
Case "title"
cal.str_Title = oneElement.Value
Case "updated"
'cal.dt_Updated =
DateTime.TryParse(oneElement.Value, cal.dt_Updated)
Case "entry"
Dim newEntry As New HalcyonCalendarEvent
newEntry.int_Attending = 0
For Each entryElement As XElement In oneElement.Descendants
Select Case entryElement.Name.LocalName
Case "title"
newEntry.str_Title = entryElement.Value
Case "updated"
newEntry.dt_Updated = DateTime.Parse(entryElement.Value)
Case "content"
newEntry.str_RawContent = entryElement.Value
Case Else
'not used
'author
End Select
Next
cal.obj_Events.Add(newEntry)
Case Else
'not used
End Select
Next
'parse event content to fill in remaining calendarEvent data
For Each oneEvent As HalcyonCalendarEvent In cal.obj_Events
'Original
'<table style="font-family:tahoma;font-size:11px"><tr><td style="vertical-align:top" align="right"><b>Start:</b></td><td style="width:10px" align="right" /><td>Thursday, August 16, 2012</td></tr><tr><td style="vertical-align:top" align="right"><b>End:</b></td><td style="width:10px" align="right" /><td>Thursday, August 16, 2012</td></tr><tr><td style="vertical-align:top" align="right"><b>Location:</b></td><td style="width:10px" align="right" /><td>St Louis</td></tr><tr><td style="vertical-align:top" align="right"><b>Who:</b></td><td style="width:10px" align="right" /><td>Aleisha Ritter;undertkr2002@yahoo.com</td></tr><tr><td style="vertical-align:top" align="right"><b>Description:</b></td><td style="width:10px" align="right" /><td /></tr></table>
'HTML replacements IN THIS ORDER!
' "find!replace"
' "find!" to leave blank
' "find!|" for separator
Dim replaceStrings() As String = {"<!<", ">!>", "<tr>!", "</tr>!", "<b>!", "</b>!", "<td style=""width:10px"" align=""right"" />!", "<td />!", "style=""vertical-align:top""!", "align=""right""|", "style=""font-family:tahoma;font-size:11px""!", "td>!", "<td!", "<table >!", "</table>!", ">!", "<!", "/td!|"}
Dim tempStr As String()
Dim tempContent As String
tempContent = oneEvent.str_RawContent
For Each oneString As String In replaceStrings
Try
tempContent = tempContent.Replace(oneString.Split("|")(0), oneString.Split("|")(1))
Catch ex As Exception
End Try
Next
'After replacements we can just split on |, trim each string, and iterate through it
'Start:|Thursday, August 16, 2012|End:|Thursday, August 16, 2012|Location:|St Louis|Who:|Aleisha Ritter|undertkr2002@yahoo.com|Description:|
tempStr = tempContent.Split("|")
Dim currentHeader As String = ""
For Each oneString As String In tempStr
'this will be a header followed by a value
' Start:
' blah blah
' End:
' blah blah
' etc...
Select Case oneString.Trim.ToLower
Case "start:"
currentHeader = oneString.Trim.ToLower
Case "end:"
currentHeader = oneString.Trim.ToLower
Case "location:"
currentHeader = oneString.Trim.ToLower
Case "who:"
currentHeader = oneString.Trim.ToLower
Case "description:"
currentHeader = "skip me"
Case Else
'This is a value instead of a header then
Select Case currentHeader
Case "start:"
oneEvent.dt_StartTime = DateTime.Parse(oneString.Trim)
Case "end:"
oneEvent.dt_EndTime = DateTime.Parse(oneString.Trim)
Case "location:"
oneEvent.str_Location = oneString.Trim
If IsAtHome(oneString) Then oneEvent.AtHome = True
Case "who:"
oneEvent.int_Attending += 1
Case "skip me"
'doesn't matter to Halcyon
Case Else
'doesn't matter to Halcyon
End Select
End Select
Next
Next
Return cal
End Function
Public Function Iterate_GMAIL()
'Because i'm iterating a shared calendar XML, we could also do the same for other providers.
'Just look within the link string of the calendar to determine provider. If it contains "microsoft.com" or "google.com" etc
Return Nothing
End Function
End Module
MainPage.xaml
XML<Page
x:Class="Halcyon_Alpha.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Halcyon_Alpha"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Text="Halcyon" VerticalAlignment="Top" Margin="0,25,0,0" TextAlignment="Center" Foreground="#FF035CD0" FontWeight="Bold" FontSize="29.333"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >
<RadioButton x:Name="radioC" Content="Celsius" GroupName="displaySetting" IsChecked="True" />
<RadioButton x:Name="radioF" Content="Farenheit" GroupName="displaySetting" FlowDirection="RightToLeft" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,25,0,0">
<TextBlock TextWrapping="Wrap" Text="Ambient Temperature:" VerticalAlignment="Top" Margin="0,0,0,0" TextAlignment="Center"/>
<TextBlock x:Name="tbTempAmbient" TextWrapping="Wrap" Text="loading..." VerticalAlignment="Top" Margin="25,0,0,0" TextAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,25,0,0">
<TextBlock TextWrapping="Wrap" Text="Desired Temperature:" VerticalAlignment="Top" Margin="0,0,0,0" TextAlignment="Center"/>
<TextBlock x:Name="tbTempSetting" TextWrapping="Wrap" Text="loading..." VerticalAlignment="Top" Margin="25,0,0,0" TextAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,25,0,0">
<TextBlock TextWrapping="Wrap" Text="Status:" VerticalAlignment="Top" Margin="0,0,0,0" TextAlignment="Center"/>
<TextBlock TextWrapping="Wrap" Text="loading..." x:Name="tbStatus" VerticalAlignment="Top" Margin="25,0,0,0" TextAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,25,0,0">
<TextBlock TextWrapping="Wrap" Text="Current Time:" VerticalAlignment="Top" Margin="0,0,0,0" TextAlignment="Center"/>
<TextBlock x:Name="tbCurrentTime" TextWrapping="Wrap" Text="loading..." VerticalAlignment="Top" Margin="25,0,0,0" TextAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,25,0,0">
<TextBlock TextWrapping="Wrap" Text="Force Temp:" VerticalAlignment="Top" Margin="0,5,0,0" TextAlignment="Center"/>
<TextBox x:Name="txtForce" TextWrapping="Wrap" Text="20" VerticalAlignment="Top" Margin="25,0,0,0" TextAlignment="Center"/>
<Button x:Name="btnForceTemp" Content="Apply" Margin="25,0,0,0"/>
</StackPanel>
<TextBlock x:Name="tbError" TextWrapping="Wrap" Text="." VerticalAlignment="Top" Margin="0,25,0,0" TextAlignment="Center" Foreground="Red"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.vb
VBScript'The MainPage code should initiate code on the other modules, and will also be responsible for handling GUI interactions and updates
''' <summary>
''' -> at the start of a comment denotes PSUEDO CODE, not an actual comments
''' </summary>
Public NotInheritable Class MainPage
Inherits Page
Private Sub MainPage_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Try
InitializeHardware() 'make sure our hardware connections work and are started up
InitializeSoftware() 'do initial sync and start our timers to make the magic happen
Catch ex As Exception
stopTimers()
tbError.Text = "ERROR: " & ex.Message
End Try
End Sub
Public Sub UpdateInterface()
Select Case radioC.IsChecked
Case True
'display Celsius
tbTempAmbient.Text = TempAmbient_Celsius.ToString
tbTempSetting.Text = TempSet_Celsius.ToString
Case False
'display Farenheit
tbTempAmbient.Text = TempAmbient_Farenheit.ToString
tbTempSetting.Text = TempSet_Farenheit.ToString
End Select
tbCurrentTime.Text = DateTime.Now.ToString
If ac_ON Then
tbStatus.Text = "Cooling"
ElseIf heat_ON Then
tbStatus.Text = "Heating"
Else
tbStatus.Text = "Everything Off"
End If
End Sub
Private Sub btnForceTemp_Click(sender As Object, e As RoutedEventArgs) Handles btnForceTemp.Click
Try
If radioC.IsChecked Then
TempSet_Celsius = Math.Round(CInt(txtForce.Text.Trim), precision)
Else
TempSet_Farenheit = Math.Round(CInt(txtForce.Text.Trim), precision)
End If
Catch ex As Exception
tbError.Text = ex.Message
txtForce.Text = "20"
End Try
End Sub
End Class
Away Events.xml
XML<?xml version="1.0" encoding="utf-8"?>
<feed xmlns:sx="http://www.microsoft.com/schemas/sse" xmlns="http://www.w3.org/2005/Atom">
<title>Away Events</title>
<subtitle>Halcyon Away Events Testing</subtitle>
<link rel="self" href="https://sharing.calendar.live.com/calendar/private/eca6cd71-ae7c-4146-b7c1-a8f9d03de0d2/0ff376e0-b10d-4067-acde-71cd450283db/cid-60e973077a985b49/calendar.xml" type="application/atom+xml" />
<updated>2015-09-16T20:49:49Z</updated>
<entry>
<title>Away - Friday, September 25, 2015 8:00AM(Eastern Standard Time)</title>
<updated>2015-09-16T20:49:19Z</updated>
<author>
<email>gd.ritter@live.com</email>
</author>
<content type="html"><table style="font-family:tahoma;font-size:11px"><tr><td style="vertical-align:top" align="right"><b>Start:</b></td><td style="width:10px" align="right" /><td>Friday, September 25, 2015 8:00AM (Eastern Standard Time)</td></tr><tr><td style="vertical-align:top" align="right"><b>End:</b></td><td style="width:10px" align="right" /><td>Friday, September 25, 2015 1:30PM (Eastern Standard Time)</td></tr><tr><td style="vertical-align:top" align="right"><b>Repeat:</b></td><td style="width:10px" align="right" /><td>Occurs every week on Friday.</td></tr><tr><td style="vertical-align:top" align="right"><b>Location:</b></td><td style="width:10px" align="right" /><td>Away</td></tr><tr><td style="vertical-align:top" align="right"><b>Who:</b></td><td style="width:10px" align="right" /><td /></tr><tr><td style="vertical-align:top" align="right"><b>Description:</b></td><td style="width:10px" align="right" /><td>Away</td></tr></table></content>
</entry>
<entry>
<title>Away - Monday, September 21, 2015 8:00AM(Eastern Standard Time)</title>
<updated>2015-09-16T20:49:48Z</updated>
<author>
<email>gd.ritter@live.com</email>
</author>
<content type="html"><table style="font-family:tahoma;font-size:11px"><tr><td style="vertical-align:top" align="right"><b>Start:</b></td><td style="width:10px" align="right" /><td>Monday, September 21, 2015 8:00AM (Eastern Standard Time)</td></tr><tr><td style="vertical-align:top" align="right"><b>End:</b></td><td style="width:10px" align="right" /><td>Monday, September 21, 2015 6:30PM (Eastern Standard Time)</td></tr><tr><td style="vertical-align:top" align="right"><b>Repeat:</b></td><td style="width:10px" align="right" /><td>Occurs every week on Monday, Tuesday, Wednesday and Thursday.</td></tr><tr><td style="vertical-align:top" align="right"><b>Location:</b></td><td style="width:10px" align="right" /><td>Away</td></tr><tr><td style="vertical-align:top" align="right"><b>Who:</b></td><td style="width:10px" align="right" /><td /></tr><tr><td style="vertical-align:top" align="right"><b>Description:</b></td><td style="width:10px" align="right" /><td>Away</td></tr></table></content>
</entry>
<timezone xmlns="http://calendar.live.com/schemas/sync/calendar"><![CDATA[BEGIN:VTIMEZONE
TZID:Eastern Standard Time
BEGIN:STANDARD
DTSTART:20061029T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZOFFSETTO:-0500
TZOFFSETFROM:-0400
END:STANDARD
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZOFFSETTO:-0500
TZOFFSETFROM:-0400
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20060402T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZOFFSETTO:-0400
TZOFFSETFROM:-0500
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZOFFSETTO:-0400
TZOFFSETFROM:-0500
END:DAYLIGHT
END:VTIMEZONE
]]></timezone>
</feed>
Comments