It is currently June 23rd, 2024, 11:14 pm

Min/Max Lua script problem

Get help with creating, editing & fixing problems with skins
Steve_E
Posts: 5
Joined: May 23rd, 2023, 9:53 am

Min/Max Lua script problem

Post by Steve_E »

Hi - first post here :

I am trying to get the min and max values of a list contained in MeasureTempHistory as follows:

Code: Select all

[Rainmeter]
Update=1000
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

[StyleLiteText]
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16

[TodaysDate]
Measure=Time
Format=%F

[MeasureScript]
Measure=Script
ScriptFile=#@#MyScript.lua
Disabled=1

[MeasureEcowittHistory]
Measure=WebParser
URL=https://api.ecowitt.net/api/v3/device/history?application_key=xxxxxxxxxxxxxxxxxx&api_key=xxxxxxxxxxxxxxxxxxxxxxxe&mac=xxxxxxxxxxxxxxxxxxxx&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
RegExp=(?siU)"list":{(.*)}}}}}.*
UpdateRate=60

[MeasureTempHistory]
Measure=WebParser
URL=[MeasureEcowittHistory]
StringIndex=1
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""

[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
Text=MaxTemp = [&MeasureScript:MaxValue('[&MeasureTempHistory]')]
DynamicVariables=1
W=240
H=80
With the Lua script as per this https://docs.rainmeter.net/snippets/min-max-value/:

Code: Select all

function MinValue(...)

	valueTable = {}
	
	for i = 1, #arg do
		table.insert(valueTable, arg[i])
	end
	
	table.sort(valueTable, function(a,b) return a<b end)
	
	return valueTable[1]

end

function MaxValue(...)

	valueTable = {}
	
	for i = 1, #arg do
		table.insert(valueTable, arg[i])
	end
	
	table.sort(valueTable, function(a,b) return a>b end)
	
	return valueTable[1]

end
Now, I am not a programmer, probably not even a good hack anymore, but I can see that MeasureTempHistory does contain a list of numbers/text in the Skins log. Yet after trying unsuccessfully all day to get the min/max value functions to return anything other than the full list of values in MeasureTempHistory I have posted here for some insight as to what might be wrong with the code or what to do to get it to work.

Cheers
Last edited by Steve_E on May 24th, 2023, 7:27 am, edited 1 time in total.
User avatar
nek
Posts: 105
Joined: November 3rd, 2019, 12:00 am

Re: Min/Max Lua script problem

Post by nek »

The problem of you code is MaxValue('[&MeasureTempHistory]').
The data type of argument is string '14.6,13.5,14.9,...'.
The MaxValue function requires arguments as number list 14.6,13.5,14.9,....
You need to get rid of the apostrophes MaxValue([&MeasureTempHistory]).

How about this?
EDITED: This code crashes Rainmeter.exe, You don't need to test this code.

Code: Select all

;; [Rainmeter] section | https://docs.rainmeter.net/manual/skins/rainmeter-section/
[Rainmeter]
Update=1000
;; @modified
DefaultUpdateDivider=-1
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

[MeasureScript]
Measure=Script
ScriptFile=#@#MyScript.lua
Disabled=1

;; %F is `2023-05-23` (YYYY-MM-DD)
;; Time measure Format Codes | https://docs.rainmeter.net/manual/measures/time/#FormatCodes
[TodaysDate]
Measure=Time
Format=%F
;; @modified
UpdateDivider=1

;; WebParser measure | https://docs.rainmeter.net/manual/measures/webparser/
[MeasureEcowittHistory]
Measure=WebParser
;; @modified for testing
; URL=https://api.ecowitt.net/api/v3/device/history?application_key=20B5F2F46B4D5474E8713C269B4FE2F5&api_key=2b0e4d11-2a9c-4f33-a672-c1cdef6bbe5e&mac=BC:FF:4D:0F:44:F2&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
URL=file://#CURRENTPATH#history.json
RegExp=(?siU)"list":{(.*)}}}}}.*
UpdateRate=60
;; @modified
UpdateDivider=1
FinishAction=[!UpdateMeter MeterMaxTemp][!Redraw]

[MeasureTempHistory]
Measure=WebParser
URL=[MeasureEcowittHistory]
StringIndex=1
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""

[StyleLiteText]
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16
;; @modified to make string meter clickable for drag & drop.
SolidColor=00000001

;; String meter | https://docs.rainmeter.net/manual/meters/string/
;; Inline Lua | https://docs.rainmeter.net/manual/lua-scripting/inline-lua/
[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
Prefix=MaxTemp =[\x20]
;; @modified. MaxValue argument from string to number list.
; Text=MaxTemp = [&MeasureScript:MaxValue('[&MeasureTempHistory]')]
Text=[&MeasureScript:MaxValue([&MeasureTempHistory])]
DynamicVariables=1
W=240
H=80
MyScript.lua

Code: Select all

function MinValue(...)

	valueTable = {...}
	table.sort(valueTable, function(a,b) return a<b end)
	return valueTable[1]

end

function MaxValue(...)

	valueTable = {...}
	table.sort(valueTable, function(a,b) return a>b end)
	return valueTable[1]

end
good luck.
Last edited by nek on May 23rd, 2023, 2:20 pm, edited 3 times in total.
User avatar
Yincognito
Rainmeter Sage
Posts: 7491
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Min/Max Lua script problem

Post by Yincognito »

Steve_E wrote: May 23rd, 2023, 10:10 amWith the Lua script as per this https://docs.rainmeter.net/snippets/min-max-value/
Note: The solution provided by nek, although similar, crashes Rainmeter for some reason after uncommenting back to the original URL, so...

The main issue here is that the said code assumes that you pass multiple, separated, individual arguments / parameters to that function, but in your case, the sole argument you're passing to the function is a string list (i.e. instead of MaxValue(1,2,3) you have MaxValue('1,2,3')). Therefore, you have to split that string list and populate your table with its elements.

However, that alone isn't enough, because if you let them be strings, they will be sorted alphabetically, meaning that '9.9' will be greater than, say, '23.4' - so you have to convert the elements to numbers as well.

Another issue is that the function execution should happen immediately after getting the data from the site, therefore a FinishAction to do that is recommended in the main WebParser measure, to trigger getting the max right after the list is available.

Somewhat related to the last point above is the fact that as long as the said list isn't yet retrieved, the skin will display the literal [&MeasureScript:MaxValue('[&MeasureTempHistory]')] as the value, since Lua will return NIL after an empty table sorting. Thus, you'll have to instruct Lua to handle that case using an or 0 in the return phase.

Lastly, to comfortably click on the entire area of the skin (and not just the thin areas of the text) and do your thing, a near transparent SolidColor=0,0,0,1 added to the style is nice to have.

With all the above in mind, your code would have to be something like...

...\@Resources\MyScript.lua:

Code: Select all

function MinValue(...)

  valueTable = {}
  sep = ','
  
  for str in string.gmatch(arg[1], '([^'..sep..']+)') do
    table.insert(valueTable, tonumber(str) or str)
  end
  
  -- for i = 1, #arg do
    -- table.insert(valueTable, arg[i])
  -- end
  
  table.sort(valueTable, function(a,b) return a<b end)
  
  return valueTable[1] or 0

end

function MaxValue(...)

  valueTable = {}
  sep = ','
  
  for str in string.gmatch(arg[1], '([^'..sep..']+)') do
    table.insert(valueTable, tonumber(str) or str)
  end
  
  -- for i = 1, #arg do
    -- table.insert(valueTable, arg[i])
  -- end
  
  table.sort(valueTable, function(a,b) return a>b end)
  
  return valueTable[1] or 0

end
...\YourSkin.ini:

Code: Select all

[Rainmeter]
Update=1000
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

[StyleLiteText]
SolidColor=0,0,0,1
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16

[TodaysDate]
Measure=Time
Format=%F

[MeasureScript]
Measure=Script
ScriptFile=#@#MyScript.lua
Disabled=1

[MeasureEcowittHistory]
Measure=WebParser
URL=https://api.ecowitt.net/api/v3/device/history?application_key=20B5F2F46B4D5474E8713C269B4FE2F5&api_key=2b0e4d11-2a9c-4f33-a672-c1cdef6bbe5e&mac=BC:FF:4D:0F:44:F2&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
RegExp=(?siU)"list":{(.*)}}}}}.*
FinishAction=[!UpdateMeasure MeasureTempHistory][!UpdateMeter MeterMaxTemp][!Redraw]
UpdateRate=60

[MeasureTempHistory]
Measure=WebParser
URL=[MeasureEcowittHistory]
StringIndex=1
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""
DynamicVariables=1

[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
Text=MaxTemp = [&MeasureScript:MaxValue('[&MeasureTempHistory]')]
DynamicVariables=1
W=240
H=80
That's about it. Nothing too hard, just some details to make it right. I let the initial code parts commented out with -- in the .lua file just in case you still need them for something else, but you can remove them if you like.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
nek
Posts: 105
Joined: November 3rd, 2019, 12:00 am

Re: Min/Max Lua script problem

Post by nek »

Yincognito wrote: May 23rd, 2023, 1:35 pm Note: The solution provided by nek, although similar, crashes Rainmeter for some reason after uncommenting back to the original URL, so...
@Yincognito Thank you! That code crashes my Rainmeter too!

I have updated the code.
It kept argument as a string MaxValue('[&MeasureTempHistory]') and created table in Lua.

Code: Select all

;; [Rainmeter] section | https://docs.rainmeter.net/manual/skins/rainmeter-section/
[Rainmeter]
Update=1000
;; @modified
DefaultUpdateDivider=-1
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

[MeasureScript]
Measure=Script
ScriptFile=#@#MyScript.lua
Disabled=1

;; %F is `2023-05-23` (YYYY-MM-DD)
;; Time measure Format Codes | https://docs.rainmeter.net/manual/measures/time/#FormatCodes
[TodaysDate]
Measure=Time
Format=%F
;; @modified
UpdateDivider=1

;; WebParser measure | https://docs.rainmeter.net/manual/measures/webparser/
[MeasureEcowittHistory]
Measure=WebParser
URL=https://api.ecowitt.net/api/v3/device/history?application_key=20B5F2F46B4D5474E8713C269B4FE2F5&api_key=2b0e4d11-2a9c-4f33-a672-c1cdef6bbe5e&mac=BC:FF:4D:0F:44:F2&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
RegExp=(?siU)"list":{(.*)}}}}}.*
;; @modified
; UpdateRate=60
UpdateRate=600
UpdateDivider=1
FinishAction=[!UpdateMeter MeterMaxTemp][!Redraw]

[MeasureTempHistory]
Measure=WebParser
URL=[MeasureEcowittHistory]
StringIndex=1
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""

[StyleLiteText]
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16
;; @modified to make string meter clickable for drag & drop.
SolidColor=0,0,0,1

;; String meter | https://docs.rainmeter.net/manual/meters/string/
;; Inline Lua | https://docs.rainmeter.net/manual/lua-scripting/inline-lua/
[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
;; @modified
; Text=MaxTemp = [&MeasureScript:MaxValue('[&MeasureTempHistory]')]
Prefix=MaxTemp =[\x20]
Text=[&MeasureScript:MaxValue('[&MeasureTempHistory]')]
DynamicVariables=1
W=240
H=80
MyScript.lua

Code: Select all

--
-- http://lua-users.org/wiki/SplitJoin
-- @param sep {string}  -  separator
-- @return    {table}   -  Lua table index start at `1` not `0`
--
function string:split(sep)
  local sep, fields = sep or ":", {}
  local pattern = string.format("([^%s]+)", sep)
  local tonumber = tonumber
  self:gsub(pattern, function(c) fields[#fields+1] = tonumber(c) or c end)
  return fields
end

function MinValue(str)

  valueTable = str:split(',')
  table.sort(valueTable, function(a,b) return a<b end)
  return valueTable[1] or 0

end

function MaxValue(str)

  valueTable = str:split(',')
  table.sort(valueTable, function(a,b) return a>b end)
  return valueTable[1] or 0

end
EDITED: from return valueTable[1] to return valueTable[1] or 0
Reason: Mentioned by Yincognito, and it looks better. Thank you! Yincognito.

It seems that the api history data recorded on every 30 minutes, 2023-05-22 01:00, 2023-05-22 01:30, 2023-05-22 02:00
Does your skin really need to create a web request on every 1 minute?
1 minute = 1000 ms * 60 * 1 Skin Update * WebParser UpdateRate * WebParser UpdateDivider

> WebParser: How UpdateRate Works

good luck again
Last edited by nek on May 23rd, 2023, 7:23 pm, edited 3 times in total.
Steve_E
Posts: 5
Joined: May 23rd, 2023, 9:53 am

Re: Min/Max Lua script problem

Post by Steve_E »

Thanks Yincognito and Nek!

Yeah, removing the apostrophes would crash rainmeter pretty quickly and I did read somewhere that without them that Lua would see the string as a variable or something which would end up crashing. Curiously it did work briefly earlier this morning when I removed them, but not for long...

The update of 60 seconds was to be the Ecowitt normal refresh rate, however, the historic data is, or should be, in 5-minute increments, so, yeah, no need to update at the rate I had set.

So, Yincognito, you certainly have made it work - wow - I can get the min and max values now and rainmeter doesn't crash after about 3 or 4 seconds or all I see is either a full string or the statement presented back at me, so thank you very much! I did think it was perhaps due to a string vs number thing, and I was wondering how the numbers as a string would sort from high to low and vice versa. I started looking at the lua script, but at that point, I knew I was getting way out of my depth!

:thumbup: Thank you both!
User avatar
Yincognito
Rainmeter Sage
Posts: 7491
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Min/Max Lua script problem

Post by Yincognito »

nek wrote: May 23rd, 2023, 2:10 pm @Yincognito Thank you! That code crashes my Rainmeter too!

I have updated the code.
Excellent - it works now, well done! :thumbup:

P.S. The literal measure syntax still appears until the data is received, so an or 0 in the return phase of the functions, like I described earlier, would make it go away and just display 0 if the list / table is not yet populated. Of course, it doesn't necessarily have to be 0, it could be or '' or anything similar.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Yincognito
Rainmeter Sage
Posts: 7491
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Min/Max Lua script problem

Post by Yincognito »

Steve_E wrote: May 23rd, 2023, 2:10 pm Thanks Yincognito and Nek!
I think I can speak for nek as well - we're glad to help, both of us! :great:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Brian
Developer
Posts: 2699
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: Min/Max Lua script problem

Post by Brian »

nek wrote: May 23rd, 2023, 1:07 pm How about this?
EDITED: This code crashes Rainmeter.exe, You don't need to test this code.
Yincognito wrote: May 23rd, 2023, 1:35 pm Note: The solution provided by nek, although similar, crashes Rainmeter for some reason after uncommenting back to the original URL, so...
nek wrote: May 23rd, 2023, 2:10 pm @Yincognito Thank you! That code crashes my Rainmeter too!
This has been fixed for the next version of Rainmeter. I guess with that many values being pushed to Lua, it was causing an internal lua stack overflow.

-Brian
User avatar
Yincognito
Rainmeter Sage
Posts: 7491
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Min/Max Lua script problem

Post by Yincognito »

Brian wrote: May 23rd, 2023, 9:51 pm This has been fixed for the next version of Rainmeter. I guess with that many values being pushed to Lua, it was causing an internal lua stack overflow.

-Brian
Them stacks seem to be everywhere - no wonder there's a site with a similar name... :lol:
Thanks for the fix - much appreciated! :rosegift:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Yincognito
Rainmeter Sage
Posts: 7491
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: Min/Max Lua script problem

Post by Yincognito »

Steve_E wrote: May 23rd, 2023, 2:10 pmI started looking at the lua script, but at that point, I knew I was getting way out of my depth!
If you still want to get out of your depth, but this time in the Rainmeter native code instead of Lua, here's the no script version, just for fun:

Code: Select all

[Variables]
V=(?:[\+\-\.\d]+,)

[Rainmeter]
Update=1000
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

---Measures---

[TodaysDate]
Measure=Time
Format=%F

[MeasureEcowittHistory]
Measure=WebParser
URL=https://api.ecowitt.net/api/v3/device/history?application_key=20B5F2F46B4D5474E8713C269B4FE2F5&api_key=2b0e4d11-2a9c-4f33-a672-c1cdef6bbe5e&mac=BC:FF:4D:0F:44:F2&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
RegExp=(?siU)"list":{(.*)}}}}}.*
StringIndex=1
UpdateRate=60
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""
FinishAction=[!UpdateMeasureGroup WebParserGroup][!UpdateMeter MeterMaxTemp][!Redraw]

[MaxFormula]
Group=WebParserGroup
Measure=String
String=[MeasureEcowittHistory]
UpdateDivider=-1
RegExpSubstitute=1
Substitute="\s+":"","(.)$":"\1,",",+":",","(#V#{129,256})":"(\1)","(#V#{65,128})":"(\1)","(#V#{33,64})":"(\1)","(#V#{17,32})":"(\1)","(#V#{9,16})":"(\1)","(#V#{5,8})":"(\1)","(#V#{3,4})":"(\1)","(#V#{2,2})":"(\1)","(,)([)]+)":"\2\1","([(])":"Max\1",",$":""
DynamicVariables=1

[Max]
Group=WebParserGroup
Measure=Calc
Formula=[MaxFormula]
UpdateDivider=-1
DynamicVariables=1

---Styles---

[StyleLiteText]
SolidColor=0,0,0,1
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16

---Meters---

[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
Text=MaxTemp = [Max]
DynamicVariables=1
W=240
H=80
The few details on how to use it are described here, after the "If more values are desired" part (P.S. my previous code in the first Spoiler on that page, although technically valid, won't work because of Rainmeter's stack limit, hence the above alternative).
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth