Announcement

Collapse
No announcement yet.

Weather (YR / met.no) script

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Weather (YR / met.no) script

    The script below gets weather details and forecasts from YR (met.no) for pressure, wind, wind direction and rain forecast for the next 12, 24 and 48 hours.

    I use it out of interest, and to control closing blinds based on 12h rain forecast.

    Complete the TODO points - you need to put your location and identity into the request.

    Then you need to create devices for the various measurements and put the references into the script (replacing the numbers 437-442 which match my system). If you don't want a measurement, just comment out the line that sets it.

    I run the script every fifteen minutes.

    Code:
    Imports System.Web
    Imports System.Net
    Imports System.IO
    Imports System.Text
    Imports Newtonsoft.Json.Linq
    
    Sub Main(args As object)
    
      ' Get JSON data from web
      ' TODO put your location in the URL parameters
      Dim url as String = "https://api.met.no/weatherapi/locationforecast/2.0/compact?altitude=350&lat=47.36676&lon=8.54171"
    
      Dim wRequest as HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
      wRequest.Method = "GET"
    
      ' TODO put your email address in here
      wRequest.UserAgent = ""
    
      Dim wResponse As HttpWebResponse = DirectCast(wRequest.GetResponse(), HttpWebResponse)
    
      Dim sResponse As String
    
      Using srRead As New StreamReader(wResponse.GetResponseStream())
        sResponse = srRead.ReadToEnd()
      End Using
    
      ' Parse JSON data
      Dim o as JObject = JObject.Parse(sResponse)
    
      ' Get current values
      Dim offset as Integer = -1
      Dim token as JToken
    
      Do
        offset = offset + 1
    token = o.SelectToken("properties.timeseries[" & offset & "]")
      Loop Until token.Value(of DateTime)("time").ToLocalTime() > Now
    
      If offset > 0 Then
        offset = offset - 1
      End If
    
      token = o.SelectToken("properties.timeseries[" & offset & "]")
      hs.WriteLog("Weather", "Current: " & token.Value(of DateTime)("time").ToLocalTime() & " (offset " & offset & ")")
    
      token = token.SelectToken("data.instant.details")
    
      Dim baro as Double = token.Value(of Double)("air_pressure_at_sea_level")
      Dim winddir as Double = token.Value(of Double)("wind_from_direction")
      Dim windspeed as Double = token.Value(of Double)("wind_speed") * 3.6
    
      hs.WriteLog("Weather", "Barometer: " & baro & " Wind direction: " & winddir & " speed: " & windspeed)
    
      ' TODO update device numbers here
    
      hs.SetDeviceValueByRef(437, baro, True)
      hs.SetDeviceValueByRef(438, windspeed, True)
      hs.SetDeviceValueByRef(439, winddir, True)
    
      ' Get rain for next 12, 24 and 48 hours
      Dim rain12 as Double = 0.0
      Dim rain24 as Double = 0.0
      Dim rain48 as Double = 0.0
    
      For i as Integer = 0 to 42 step 6
    
        Dim rainslice as Double = o.SelectToken("properties.timeseries[" & (i+offset) & "].data.next_6_hours.details").Value(of Double)("precipitation_amount")
    
        If i <= 6 Then
          rain12 = rain12 + rainslice
        End If
    
        If i <= 18 Then
          rain24 = rain24 + rainslice
        End If
    
        rain48 = rain48 + rainslice
      Next
    
      hs.WriteLog("Weather", "Rain 12h: " & rain12 & " 24h: " & rain24 & " 48h: " & rain48)
    
      ' TODO update device numbers here
    
      hs.SetDeviceValueByRef(440, rain12, True)
      hs.SetDeviceValueByRef(441, rain24, True)
      hs.SetDeviceValueByRef(442, rain48, True)
    End Sub
    See https://api.met.no/weatherapi/locati...a/get_complete for full API details.

    Units are hPa, km/h, degrees and mm.
    Last edited by rge; August 15, 2020, 01:14 AM. Reason: Fixed api url

    #2
    Your url has &lat=47.36676,8.54171
    I think it should be &lat=47.36676&lon=8.54171

    Comment


      #3
      Another note - instead of parsing json reply manually - it's much easier and cleaner to de-serialise the json into proper class object.

      I use https://www.jsonutils.com/ to generate classes from json string, which in your case gives:

      Code:
      Weather w = JsonConvert.DeserializeObject<Weather>(sResponse);
      Code:
          public class Geometry
          {
              public string type { get; set; }
              public IList<double> coordinates { get; set; }
          }
      
          public class Units
          {
              public string air_pressure_at_sea_level { get; set; }
              public string air_temperature { get; set; }
              public string cloud_area_fraction { get; set; }
              public string precipitation_amount { get; set; }
              public string relative_humidity { get; set; }
              public string wind_from_direction { get; set; }
              public string wind_speed { get; set; }
          }
      
          public class Meta
          {
              public DateTime updated_at { get; set; }
              public Units units { get; set; }
          }
      
          public class Details
          {
              public double air_pressure_at_sea_level { get; set; }
              public double air_temperature { get; set; }
              public double cloud_area_fraction { get; set; }
              public double dew_point_temperature { get; set; }
              public double relative_humidity { get; set; }
              public double wind_from_direction { get; set; }
              public double wind_speed { get; set; }
          }
      
          public class Instant
          {
              public Details details { get; set; }
          }
      
          public class Summary
          {
              public string symbol_code { get; set; }
          }
      
          public class Next12Hours
          {
              public Summary summary { get; set; }
          }
      
          public class Next1Hours
          {
              public  summary { get; set; }
              public  details { get; set; }
          }
      
          public class Next6Hours
          {
              public  summary { get; set; }
              public  details { get; set; }
          }
      
          public class Data
          {
              public Instant instant { get; set; }
              public Next12Hours next_12_hours { get; set; }
              public Next1Hours next_1_hours { get; set; }
              public Next6Hours next_6_hours { get; set; }
          }
      
          public class Timesery
          {
              public DateTime time { get; set; }
              public Data data { get; set; }
          }
      
          public class Properties
          {
              public Meta meta { get; set; }
              public IList<Timesery> timeseries { get; set; }
          }
      
          public class Weather
          {
              public string type { get; set; }
              public Geometry geometry { get; set; }
              public Properties properties { get; set; }
          }

      Comment


        #4
        Thanks alexbk66, I fixed the URL.

        The Newtonsoft parser seems to be pre-installed with HomeSeer which is easier, and also I found jsonutils was slower and used more memory which wasn't great on a Raspberry.

        Comment


          #5
          Originally posted by rge View Post
          Thanks alexbk66, I fixed the URL.

          The Newtonsoft parser seems to be pre-installed with HomeSeer which is easier, and also I found jsonutils was slower and used more memory which wasn't great on a Raspberry.
          My code uses Newtonsoft, everybody does. Just a different function JsonConvert.DeserializeObject<T>

          https://www.newtonsoft.com/json/help...eObject__1.htm

          Comment


            #6
            Originally posted by rge View Post
            Thanks alexbk66, I fixed the URL.

            I found jsonutils was slower and used more memory which wasn't great on a Raspberry.
            www.jsonutils.com is used only once - you paste your JSON string on the website - and it generates the class for you (shown in my previous post). It's just so easy and beautiful.

            Comment


              #7
              Ah cool thanks, that wasn't the jsonutils I looked at! Makes sense, I'll maybe give it a try.

              Comment


                #8
                Unfortunately JsonUtils doesn't like having two different "details" class - it ignores the second one, and you get a missing type:

                Code:
                    public class Next1Hours
                    {
                        public  summary { get; set; }
                        public  details { get; set; }
                    }
                Once I fixed that it looks good and the code is much neater, thanks alexbk66 for your suggestion 😀

                I'll run it for a couple of days and post if it doesn't break...

                Comment


                  #9
                  After some great pointers from alexbk66 I've updated the script to use proper classes and JSON deserialization.

                  Code:
                  Imports System.Web
                  Imports System.Net
                  Imports System.IO
                  Imports System.Text
                  Imports Newtonsoft.Json
                  
                  Sub Main(args As object)
                  
                    ' Get JSON data from web
                    ' TODO put your location in the URL parameters
                    Dim url as String = "https://api.met.no/weatherapi/locationforecast/2.0/compact?altitude=300&lat=47.36676&lon=8.54171"
                  
                    Dim wRequest as HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
                    wRequest.Method = "GET"
                  
                    ' TODO put your email address in here
                    wRequest.UserAgent = ""
                  
                    Dim wResponse As HttpWebResponse = DirectCast(wRequest.GetResponse(), HttpWebResponse)
                  
                    Dim sResponse As String
                  
                    Using srRead As New StreamReader(wResponse.GetResponseStream())
                      sResponse = srRead.ReadToEnd()
                    End Using
                  
                    ' Parse JSON data
                    Dim w as Weather = JsonConvert.DeserializeObject(Of Weather)(sResponse)
                  
                    ' Get current values
                    Dim offset as Integer = -1
                  
                    Do
                      offset = offset + 1
                    Loop Until w.properties.timeseries(offset).time.ToLocalTime() > Now Or offset = w.properties.timeseries.Length - 1
                  
                    If offset = w.properties.timeseries.Length - 1 Then
                      hs.WriteLog("Weather", "ERROR: current weather timeseries not found")
                      Exit Sub
                    End If
                  
                    If offset > 0 Then
                      offset = offset - 1
                    End If
                  
                    With w.properties.timeseries(offset)
                  
                      hs.WriteLog("Weather", "Current: " & .time.ToLocalTime() & " (offset " & offset & ")")
                  
                      With .data.instant.details
                  
                        hs.WriteLog("Weather", "Barometer: " & .air_pressure_at_sea_level & " Wind direction: " & .wind_from_direction & " speed: " & .wind_speed)
                  
                        ' TODO update device numbers here
                  
                        hs.SetDeviceValueByRef(437, .air_pressure_at_sea_level, True)
                        hs.SetDeviceValueByRef(438, .wind_speed, True)
                        hs.SetDeviceValueByRef(439, .wind_from_direction, True)
                  
                      End With
                    End With
                  
                    ' Get rain for next 12, 24 and 48 hours
                    Dim rain12 as Double = 0.0
                    Dim rain24 as Double = 0.0
                    Dim rain48 as Double = 0.0
                  
                    For i as Integer = 0 to 42 step 6
                  
                      Dim rainslice as Double = w.properties.timeseries(i+offset).data.next_6_hours.details.precipitation_amount
                  
                      If i <= 6 Then
                        rain12 = rain12 + rainslice
                      End If
                  
                      If i <= 18 Then
                        rain24 = rain24 + rainslice
                      End If
                  
                      rain48 = rain48 + rainslice
                    Next
                  
                    hs.WriteLog("Weather", "Rain 12h: " & rain12 & " 24h: " & rain24 & " 48h: " & rain48)
                  
                    ' TODO update device numbers here
                  
                    hs.SetDeviceValueByRef(440, rain12, True)
                    hs.SetDeviceValueByRef(441, rain24, True)
                    hs.SetDeviceValueByRef(442, rain48, True)
                  End Sub
                  
                  Public Class Geometry
                    Public Property type As String
                    Public Property coordinates As Double()
                  End Class
                  
                  Public Class Units
                    Public Property air_pressure_at_sea_level As String
                    Public Property air_temperature As String
                    Public Property cloud_area_fraction As String
                    Public Property precipitation_amount As String
                    Public Property relative_humidity As String
                    Public Property wind_from_direction As String
                    Public Property wind_speed As String
                  End Class
                  
                  Public Class Meta
                    Public Property updated_at As DateTime
                    Public Property units As Units
                  End Class
                  
                  Public Class Details
                    Public Property air_pressure_at_sea_level As Double
                    Public Property air_temperature As Double
                    Public Property cloud_area_fraction As Double
                    Public Property dew_point_temperature As Double
                    Public Property relative_humidity As Double
                    Public Property wind_from_direction As Double
                    Public Property wind_speed As Double
                  End Class
                  
                  Public Class Details2
                    Public Property precipitation_amount as Double
                  End Class
                  
                  Public Class Instant
                    Public Property details As Details
                  End Class
                  
                  Public Class Summary
                    Public Property symbol_code As String
                  End Class
                  
                  Public Class Next12Hours
                    Public Property summary As Summary
                  End Class
                  
                  Public Class Next1Hours
                    Public Property summary As Summary
                    Public Property details As Details2
                  End Class
                  
                  Public Class Next6Hours
                    Public Property summary As Summary
                    Public Property details As Details2
                  End Class
                  
                  Public Class Data
                    Public Property instant As Instant
                    Public Property next_12_hours As Next12Hours
                    Public Property next_1_hours As Next1Hours
                    Public Property next_6_hours As Next6Hours
                  End Class
                  
                  Public Class Timesery
                    Public Property time As DateTime
                    Public Property data As Data
                  End Class
                  
                  Public Class Properties
                    Public Property meta As Meta
                    Public Property timeseries As Timesery()
                  End Class
                  
                  Public Class Weather
                    Public Property type As String
                    Public Property geometry As Geometry
                    Public Property properties As Properties
                  End Class

                  Comment


                    #10
                    I assume you left stopwatch in by mistake?
                    Jon

                    Comment


                      #11
                      Originally posted by jon00 View Post
                      I assume you left stopwatch in by mistake?
                      Yes, thanks! Was timing the script to check performance vs the self-parsing option - it's basically the same (just 1ms slower) on my system.

                      Comment


                        #12
                        Originally posted by rge View Post

                        Yes, thanks! Was timing the script to check performance vs the self-parsing option - it's basically the same (just 1ms slower) on my system.
                        Even if using proper parsing is slower - it shouldn't prevent you from using it. That's your development time and quality/reliability matter!

                        Comment


                          #13
                          Originally posted by rge View Post
                          After some great pointers from alexbk66 I've updated the script to use proper classes and JSON deserialization.

                          Code:
                          Imports System.Web
                          Imports System.Net
                          Imports System.IO
                          Imports System.Text
                          Imports Newtonsoft.Json
                          
                          Sub Main(args As object)
                          
                          ' Get JSON data from web
                          ' TODO put your location in the URL parameters
                          Dim url as String = "https://api.met.no/weatherapi/locationforecast/2.0/compact?altitude=300&lat=47.36676&lon=8.54171"
                          
                          Dim wRequest as HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
                          wRequest.Method = "GET"
                          
                          ' TODO put your email address in here
                          wRequest.UserAgent = ""
                          
                          Dim wResponse As HttpWebResponse = DirectCast(wRequest.GetResponse(), HttpWebResponse)
                          
                          Dim sResponse As String
                          
                          Using srRead As New StreamReader(wResponse.GetResponseStream())
                          sResponse = srRead.ReadToEnd()
                          End Using
                          
                          ' Parse JSON data
                          Dim w as Weather = JsonConvert.DeserializeObject(Of Weather)(sResponse)
                          
                          ' Get current values
                          Dim offset as Integer = -1
                          
                          Do
                          offset = offset + 1
                          Loop Until w.properties.timeseries(offset).time.ToLocalTime() > Now Or offset = w.properties.timeseries.Length - 1
                          
                          If offset = w.properties.timeseries.Length - 1 Then
                          hs.WriteLog("Weather", "ERROR: current weather timeseries not found")
                          Exit Sub
                          End If
                          
                          If offset > 0 Then
                          offset = offset - 1
                          End If
                          
                          With w.properties.timeseries(offset)
                          
                          hs.WriteLog("Weather", "Current: " & .time.ToLocalTime() & " (offset " & offset & ")")
                          
                          With .data.instant.details
                          
                          hs.WriteLog("Weather", "Barometer: " & .air_pressure_at_sea_level & " Wind direction: " & .wind_from_direction & " speed: " & .wind_speed)
                          
                          ' TODO update device numbers here
                          
                          hs.SetDeviceValueByRef(437, .air_pressure_at_sea_level, True)
                          hs.SetDeviceValueByRef(438, .wind_speed, True)
                          hs.SetDeviceValueByRef(439, .wind_from_direction, True)
                          
                          End With
                          End With
                          
                          ' Get rain for next 12, 24 and 48 hours
                          Dim rain12 as Double = 0.0
                          Dim rain24 as Double = 0.0
                          Dim rain48 as Double = 0.0
                          
                          For i as Integer = 0 to 42 step 6
                          
                          Dim rainslice as Double = w.properties.timeseries(i+offset).data.next_6_hours.details.precipitation_amount
                          
                          If i <= 6 Then
                          rain12 = rain12 + rainslice
                          End If
                          
                          If i <= 18 Then
                          rain24 = rain24 + rainslice
                          End If
                          
                          rain48 = rain48 + rainslice
                          Next
                          
                          hs.WriteLog("Weather", "Rain 12h: " & rain12 & " 24h: " & rain24 & " 48h: " & rain48)
                          
                          ' TODO update device numbers here
                          
                          hs.SetDeviceValueByRef(440, rain12, True)
                          hs.SetDeviceValueByRef(441, rain24, True)
                          hs.SetDeviceValueByRef(442, rain48, True)
                          End Sub
                          
                          Public Class Geometry
                          Public Property type As String
                          Public Property coordinates As Double()
                          End Class
                          
                          Public Class Units
                          Public Property air_pressure_at_sea_level As String
                          Public Property air_temperature As String
                          Public Property cloud_area_fraction As String
                          Public Property precipitation_amount As String
                          Public Property relative_humidity As String
                          Public Property wind_from_direction As String
                          Public Property wind_speed As String
                          End Class
                          
                          Public Class Meta
                          Public Property updated_at As DateTime
                          Public Property units As Units
                          End Class
                          
                          Public Class Details
                          Public Property air_pressure_at_sea_level As Double
                          Public Property air_temperature As Double
                          Public Property cloud_area_fraction As Double
                          Public Property dew_point_temperature As Double
                          Public Property relative_humidity As Double
                          Public Property wind_from_direction As Double
                          Public Property wind_speed As Double
                          End Class
                          
                          Public Class Details2
                          Public Property precipitation_amount as Double
                          End Class
                          
                          Public Class Instant
                          Public Property details As Details
                          End Class
                          
                          Public Class Summary
                          Public Property symbol_code As String
                          End Class
                          
                          Public Class Next12Hours
                          Public Property summary As Summary
                          End Class
                          
                          Public Class Next1Hours
                          Public Property summary As Summary
                          Public Property details As Details2
                          End Class
                          
                          Public Class Next6Hours
                          Public Property summary As Summary
                          Public Property details As Details2
                          End Class
                          
                          Public Class Data
                          Public Property instant As Instant
                          Public Property next_12_hours As Next12Hours
                          Public Property next_1_hours As Next1Hours
                          Public Property next_6_hours As Next6Hours
                          End Class
                          
                          Public Class Timesery
                          Public Property time As DateTime
                          Public Property data As Data
                          End Class
                          
                          Public Class Properties
                          Public Property meta As Meta
                          Public Property timeseries As Timesery()
                          End Class
                          
                          Public Class Weather
                          Public Property type As String
                          Public Property geometry As Geometry
                          Public Property properties As Properties
                          End Class
                          Trying to test the script but i get a lot of errors like these:

                          Code:
                          Property without a 'ReadOnly' or 'WriteOnly' specifier must provide both a 'Get' and a 'Set'
                          Maybe related to my scriptingreferencese?
                          Code:
                          ScriptingReferences=Newtonsoft.Json;bin\homeseer\Newtonsoft.Json.dll
                          Any suggestions?

                          Code:
                          Date/time=26/02/2021 09:31:52 CET
                          Version=HS4 Standard Edition 4.1.12.0 (Linux)
                          MONO Version=Mono JIT compiler version 6.8.0.105 (Debian 6.8.0.105+dfsg-3 Mon Apr 27 19:25:46 UTC 2020)
                          
                          License=Registered
                          Confguration File=/opt/HomeSeer/Data/HomeSeerData.json
                          Uptime=0 Days 17 Hours 52 Minutes 27 Seconds
                          Lan IP=10.0.0.191 (ubuntu)
                          Device Count=296
                          Event Count=23
                          Plugins Enabled=Z-Wave:,JowiHue:
                          Modules/Threads=89 Modules, 49 Threads
                          Available Threads=399
                          HomeSeer Memory Used=169 Mbytes
                          Plugins Installed=JowiHue 2.1.1.9,Z-Wave 3.0.2.0

                          Comment


                            #14
                            Originally posted by widert View Post
                            Code:
                            Property without a 'ReadOnly' or 'WriteOnly' specifier must provide both a 'Get' and a 'Set'
                            I'm not expert in Linux and VB.NET but I suspect it's caused by Mono. Because it's nothing wrong with auto-implemented properties. I suggest to Google for this error in Mono

                            Comment


                              #15
                              Originally posted by alexbk66 View Post

                              I'm not expert in Linux and VB.NET but I suspect it's caused by Mono. Because it's nothing wrong with auto-implemented properties. I suggest to Google for this error in Mono
                              As far as I can read from the above then RGE had the same issues, but solved them.

                              Comment

                              Working...
                              X