Wow this looks great. I would love to see the node red flow and how you got this data so beautifully display in HA if possible
I note that the canvas gauge card is deprecated and receiving no more support. GitHub - custom-cards/canvas-gauge-card: The card makes it possible to use gauges from https://canvas-gauges.com/
Bummer, anyone know of an alternative? Or want to take over maintenance?
I am curious - has the card stopped working in the latest HA versions - or there are no updates for last XX months?
In issues I noticed a possibly important issue with Companion App; other issues seem to be less critical.
There are some other popular cards which were updated a few years ago (like stack-in-card) but they still workā¦
It still works for my purposes, I was just reading what they said on github.
I will start testing this card. It does not seem like āoutdatableā. Users may not get new features ofc.
If anyone is interested, hereās the configuration.yaml edits if youād like to use NOAA Tides instead of the World Tides sensor.
# NOAA Tides
# https://www.home-assistant.io/integrations/noaa_tides/
# https://tidesandcurrents.noaa.gov/noaatidepredictions.html?id=8579629
sensor:
- platform: noaa_tides
station_id: 8579629
# Station ID should be changed depending on where you are monitoring tides
# low_tide_time: 2023-12-29T03:28
# low_tide_height: -0.312
# high_tide_time: 2023-12-29T09:48
# high_tide_height: 1.791
# attribution: Data provided by NOAA
# friendly_name: NOAA Tides
template:
- sensor:
- name: "tide pos"
unit_of_measurement: "%"
state: >
{% set high = as_timestamp(state_attr('sensor.noaa_tides', 'high_tide_time'))|float %}
{% set low = as_timestamp(state_attr('sensor.noaa_tides', 'low_tide_time'))|float %}
{% if (high - low) > 0 %}
{{50-(((low - as_timestamp(now()))/(high -low)) * 50)}}
{% else %}
{{100 - (((high - as_timestamp(now()))/(low-high)) * 50)}}
{%endif %}
Sorry for the delay Gareth, not sure why I didnāt get a notification of your comment.
Anyway happy to share so here is the code for the Canvas guage.card
type: custom:canvas-gauge-card
entity: sensor.tideclock2
card_height: 340
gauge:
animation: true
animationTarget: needle
type: radial-gauge
title: Tide
width: 340
height: 340
borderShadowWidth: 4
borderOuterWidth: 1
borderMiddleWidth: 1
borderInnerWidth: 4
minValue: 0
maxValue: 100
startAngle: 180
ticksAngle: 360
valueBox: false
toolbar:
show: false
majorTicks:
- High
- 1/5
- 2/4
- Falling
- 4/2
- 5/1
- Low
- 1/5
- 2/4
- Rising
- 4/2
- 5/1
- High
minorTicks: 12
strokeTicks: true
borders: true
needleType: arrow
needleWidth: 3
colorNeedle: rgb(77, 84, 92, .90)
colorNeedleEnd: rgba(150, 161, 176, 90)
needleShadow: true
colorNeedleShadowUp: rgba(209, 223, 240,.90)
colorNeedleShadowDown: rgba(77, 84, 92, .10)
colorNeedleCircleOuterEnd: rgba(209, 223, 240)
colorNeedleCircleOuter: rgba(77, 84, 92)
colorNeedleCircleInnerEnd: rgba(77, 84, 92)
colorNeedleCircleInner: rgba(209, 223, 240)
colorPlate: rgba(173, 184, 199, .30)
colorPlateEnd: rgba(209, 223, 240, .10)
colorBorderInnerEnd: rgb(225,225,225)
colorBorderInner: rgb(140,140,140)
colorBorderMiddle: rgb(186,186,186)
colorBorderMiddleEnd: rgb(115,115,115)
colorBorderOuterEnd: rgb(120,120,120)
colorBorderOuter: rgb(245,245,245)
highlights:
- from: 25
to: 33.3
color: rgba(24, 145, 3, .40)
- from: 33.3
to: 41.5
color: rgba(24, 145, 3, .70)
- from: 41.5
to: 50
color: rgba(24, 145, 3, .90)
- from: 66.8
to: 75
color: rgba(24, 145, 3, .40)
- from: 58.4
to: 66.8
color: rgba(24, 145, 3, .70)
- from: 50
to: 58.4
color: rgba(24, 145, 3, .90)
- from: 75
to: 83.5
color: rgba(21, 171, 171, .40)
- from: 83.5
to: 91.7
color: rgba(144, 186, 252, .70)
- from: 91.7
to: 100
color: rgba(144, 186, 252, .99)
- from: 0
to: 8.5
color: rgba(144, 186, 252, .99)
- from: 8.5
to: 16.8
color: rgba(144, 186, 252, .70)
- from: 16.8
to: 25
color: rgba(21, 171, 171, .40)
useMinPath: true
I have absolutely no issues with the canvas guage card and I am also using it with my website in an html page having created a tide page for my use, when I am out at sea.
The Node-Red flow has become rather complicated because I keep adding bits
Part 1 is the table flow, which also adjusts to local time, (you might have to change the it in the function to suit). There is more info there than you really need, but I just added to it for completeness. You will need to put your API key in the UKHO Data Get.
PART2
is the output to a share on my Pi as an XML file and then FTPout to my website
PART 3
Is the Calc for the Tideclock
Part 4
Is the calculation using the 12ths rule of the tide height every 5 minutes
This is also sent to my website as an XML file
I am not very good at Javascript yet so I expect you geniuses out there will make a better job of the functions.
Just out of interest I asked ChatGPT to do the Javascript for the 12ths Rule and after 20 attrempts that were completely wrong I gave up and did it myself.
[{"id":"862178c64ab775c7","type":"group","z":"6a77ed22f5291f7a","name":"GET TABLE","style":{"stroke":"#001f60","label":true,"color":"#0070c0","label-position":"n"},"nodes":["dcf34aab.218928","bccbb3e69cdab71d","d501796bb392d9cf","307d024eb3c89b95","51661ba71299b073","8897526cf8527a12","ab2a5f7c0d08271a"],"x":54,"y":99,"w":1052,"h":82},{"id":"dcf34aab.218928","type":"inject","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"Hourly","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10800","crontab":"","once":true,"onceDelay":"","topic":"","payload":"","payloadType":"date","x":160,"y":140,"wires":[["ab2a5f7c0d08271a"]]},{"id":"bccbb3e69cdab71d","type":"function","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"function 49","func":"var newArray;\nvar number = 0;\nfor(var i = 0; i < msg.payload.length; i++) {\n var newMsg = {};\n newMsg.payload = {\n tide_id: [i],\n event: msg.payload[i].EventType,\n daytime: msg.payload[i].DateTime,\n height: Number(msg.payload[i].Height.toFixed(2)),\n date: msg.payload[i].Date,\n offset: Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset())/60,\n hrlocale: new Date(msg.payload[i].DateTime).getHours() + (Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset()) / 60),\n UKstamp: new Date(msg.payload[i].DateTime).setHours(new Date(msg.payload[i].DateTime).getHours() + (Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset()) / 60)),\n GMTstamp: new Date(msg.payload[i].DateTime).getTime(),\n GMTtime: new Date(msg.payload[i].DateTime).toLocaleString('en-GB'),\n UKtime: new Date(new Date(msg.payload[i].DateTime).setHours(new Date(msg.payload[i].DateTime).getHours() + (Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset()) / 60))).toLocaleString('en-GB'),\n day: new Date(new Date(msg.payload[i].DateTime).setHours(new Date(msg.payload[i].DateTime).getHours() + (Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset()) / 60))).toLocaleString('en-GB').substr(0, 10),\n time: new Date(new Date(msg.payload[i].DateTime).setHours(new Date(msg.payload[i].DateTime).getHours() + (Math.abs(new Date(msg.payload[i].DateTime).getTimezoneOffset()) / 60))).toLocaleString('en-GB').substr(12, 5),\n \n }\n\n node.send(newMsg);\n}\nreturn null; ","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":140,"wires":[["d501796bb392d9cf"]]},{"id":"d501796bb392d9cf","type":"function","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"together","func":"\n//if (msg.payload.event == \"HighWater\"){\n// msg.payload.event = \"High-Water\"\n// } else {\n// msg.payload.event = \"Low-Water\"\n//}\n\nmsg.tide = {\n tide_id: msg.payload.tide_id,\n day: msg.payload.day,\n time: msg.payload.time,\n height: msg.payload.height,\n event: msg.payload.event,\n\n}\n\nreturn msg; ","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":140,"wires":[["307d024eb3c89b95"]]},{"id":"307d024eb3c89b95","type":"change","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"tide-pay","rules":[{"t":"set","p":"payload","pt":"msg","to":"tide","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":780,"y":140,"wires":[["51661ba71299b073"]]},{"id":"51661ba71299b073","type":"change","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"AP","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"Tides\" : {\"tide_id\" : [payload.tide_id],\"day\" : [payload.day],\"time\" : [payload.time],\"height\" : [payload.height],\"event\" : [payload.event]}}","tot":"jsonata"},{"t":"set","p":"topic","pt":"msg","to":"data","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":140,"wires":[["8897526cf8527a12"]]},{"id":"8897526cf8527a12","type":"join","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"2","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":1030,"y":140,"wires":[["b7f3b360e3caf235"]]},{"id":"ab2a5f7c0d08271a","type":"http request","z":"6a77ed22f5291f7a","g":"862178c64ab775c7","name":"UKHO data","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://admiraltyapi.azure-api.net/uktidalapi/api/V1/Stations/0020/TidalEvents?","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"other","keyValue":"Host","valueType":"other","valueValue":"admiraltyapi.azure-api.net"},{"keyType":"other","keyValue":"Ocp-Apim-Subscription-Key","valueType":"other","valueValue":""}],"x":330,"y":140,"wires":[["bccbb3e69cdab71d"]]},{"id":"d17ca5b2a98b966a","type":"group","z":"6a77ed22f5291f7a","name":"FTP OUT TO WEBSITE","style":{"stroke":"#0070c0","label":true,"color":"#001f60","label-position":"n"},"nodes":["4d964a0243383e7c","06aa539312f8eda2","b7f3b360e3caf235","f8f84a837e5628df","2acaa0172d62d403","db1d5343739224c5","ddd715c50676500b","73c1ef0e7bc92a78"],"x":294,"y":239,"w":812,"h":122},{"id":"4d964a0243383e7c","type":"file","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"Output","filename":"/share/stidedata.xml","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"setbymsg","x":810,"y":280,"wires":[["73c1ef0e7bc92a78"]]},{"id":"06aa539312f8eda2","type":"xml","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"","property":"payload","attr":"$","chr":"_","x":610,"y":280,"wires":[["4d964a0243383e7c"]]},{"id":"b7f3b360e3caf235","type":"change","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"Tidelist","rules":[{"t":"set","p":"payload","pt":"msg","to":"{ \"Tidelist\" : payload }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":280,"wires":[["06aa539312f8eda2"]]},{"id":"f8f84a837e5628df","type":"comment","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"FORMAT DATA","info":"","x":400,"y":320,"wires":[]},{"id":"2acaa0172d62d403","type":"comment","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"CONVERT XML","info":"Just add the heading for the XML file","x":600,"y":320,"wires":[]},{"id":"db1d5343739224c5","type":"comment","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"OUTPUT TO PI","info":"The datafile as an XML file is Output\nto the share folder of HASS on my Pi","x":820,"y":320,"wires":[]},{"id":"ddd715c50676500b","type":"comment","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","name":"FTP TO WEB","info":"Enter FTP details of your website","x":1010,"y":320,"wires":[]},{"id":"73c1ef0e7bc92a78","type":"ftp in","z":"6a77ed22f5291f7a","g":"d17ca5b2a98b966a","ftp":"ce7394158a346155","operation":"put","filename":"xxxxxxx","localFilename":"/share/stidedata.xml","name":"","x":1010,"y":280,"wires":[[]]},{"id":"ce7394158a346155","type":"ftp","host":"","port":"","secureOptions":"","user":"","connTimeout":"","pasvTimeout":"","keepalive":""},{"id":"87853723e6aabe04","type":"group","z":"6a77ed22f5291f7a","name":"TIDECLOCK","style":{"stroke":"#3f93cf","label":true,"color":"#001f60","label-position":"n"},"nodes":["320a5b845e0284ad","edec4cc9bb8d2e0c","4bb1dd113f0889f2","52a8326be8736fd5","386f8d2167c52cff","96ec510f6df4ecc4","dbe5ac0b1cae2708","e6cb8fd0e3786138","dfb5b1448f13f1eb"],"x":94,"y":439,"w":1152,"h":122},{"id":"320a5b845e0284ad","type":"ha-sensor","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"tideclock2","entityConfig":"fc1b6e026fc881e2","version":0,"state":"payload.clockset","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1160,"y":480,"wires":[[]]},{"id":"edec4cc9bb8d2e0c","type":"inject","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"120","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":480,"wires":[["96ec510f6df4ecc4"]]},{"id":"4bb1dd113f0889f2","type":"join","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"6","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":690,"y":480,"wires":[["52a8326be8736fd5"]]},{"id":"52a8326be8736fd5","type":"function","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"Choose tide","func":"\nfor (var i = 0; i < 5; i++) {\n\n if (msg.payload[i].nowstamp > msg.payload[i].stamp && msg.payload[i].nowstamp < msg.payload[i+1].stamp){\n var GMTdaytimelast = msg.payload[i].daytime\n var GMTdaytimenext = msg.payload[i+1].daytime\n var NextTime = msg.payload[i+1].stamp\n var LastTime = msg.payload[i].stamp\n var LastTide = msg.payload[i].height\n var NextTide = msg.payload[i+1].height\n var now = msg.payload[i].nowstamp\n var number = i +1\n }else{ }\n \n \n if (LastTide < NextTide){\n var event =\"HighWater\"\n var state =\"Rising\"\n }else{\n var event =\"LowWater\"\n var state = \"Falling\"\n }\n\n\n}\n\nmsg.payload = {\n GMTdaytimelast: GMTdaytimelast,\n GMTdaytimenext: GMTdaytimenext,\n number: number, \n now: now,\n NextTime: NextTime,\n LastTime: LastTime,\n LastTide: LastTide,\n NextTide: NextTide,\n event: event,\n state: state,\n \n} \n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":480,"wires":[["386f8d2167c52cff"]]},{"id":"386f8d2167c52cff","type":"function","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"Calculate","func":"var event = msg.payload.event;\nvar state = msg.payload.state;\nvar nowtime = msg.payload.now;\n\n\nif (msg.payload.event == \"LowWater\") {\n var lowtime = msg.payload.NextTime\n var hightime = msg.payload.LastTime\n var cycle = (msg.payload.NextTime - msg.payload.LastTime)/ (1000 * 3600)\n var waterhigh = msg.payload.LastTide\n var waterlow = msg.payload.NextTide\n var range = msg.payload.LastTide - msg.payload.NextTide\n var elapsed = (msg.payload.now - msg.payload.LastTime)/ (1000 * 3600)\n var LastTideTime = (msg.payload.LastTime)// /(1000 * 3600)\n var NextTideTime = (msg.payload.NextTime)// /(1000 * 3600)\n\n} else {\n var lowtime = msg.payload.LastTime\n var hightime = msg.payload.NextTime\n var waterhigh = msg.payload.NextTide\n var cycle = (msg.payload.NextTime - msg.payload.LastTime)/ (1000 * 3600)\n var waterlow = msg.payload.LastTide\n var range = msg.payload.NextTide - msg.payload.LastTide\n var elapsed = (msg.payload.now - msg.payload.LastTime)/ (1000 * 3600)\n var LastTideTime = (msg.payload.LastTime)// /(1000 * 3600)\n var NextTideTime = (msg.payload.NextTime)// /(1000 * 3600)\n}\n\n//SET THE CLOCK AND WATER %\nvar heightnow = 0;\nvar fallpercent = 0;\nvar wflow;\nif (lowtime - hightime > 0) {\n var tide = (((nowtime - hightime)) / (lowtime - hightime)) * 100\n heightnow = ((waterhigh - waterlow) * (100 - tide) / 100) + waterlow //correct\n fallpercent = (100 - tide) / 100\n var clockset = ((((nowtime - hightime)) / (lowtime - hightime)) * 100) / 2 //percent correct\n var num = 0\n wflow = (waterhigh - waterlow) * tide / 100;\n} else {\n var tide = 100 - ((((nowtime - hightime)) / (lowtime - hightime)) * 100) / 2\n heightnow = (waterhigh * (tide / 100));\n\n var clockset = 100 - ((((nowtime - hightime)) / (lowtime - hightime)) * 100) / 2 //percent correct\n\n wflow = (waterhigh - waterlow) * tide / 100;\n heightnow = waterlow + wflow;\n var num = 1\n}\n\n//TIDY UP\ntide = Math.floor(tide*100)/100;\nvar k_water = Math.floor((heightnow - 1.06)*100)/100;\nheightnow = Math.floor((heightnow) * 100) / 100;\nwflow = Math.floor((wflow) * 100) / 100;\n//send data\n//var tideflow = {\n//}\n\nmsg.payload = {\n tidestate: state,\n number: msg.payload.number,\n clockset: clockset,\n tide: tide,\n fallpercent: fallpercent,\n heightnow: heightnow,\n wflow: wflow,\n waterhigh: waterhigh,\n waterlow: waterlow,\n event: event,\n num: num,\n hightime: hightime,\n lowtime: lowtime,\n range: range,\n elapsed: elapsed,\n cycle: cycle,\n LastTideTime: LastTideTime,\n NextTideTime: NextTideTime,\n NextTide: msg.payload.NextTide,\n LastTide: msg.payload.LastTide,\n TideHeight: heightnow,\n k_water: k_water\n}\nreturn msg;\n\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1000,"y":480,"wires":[["320a5b845e0284ad","f8472beab0dae8ec"]]},{"id":"96ec510f6df4ecc4","type":"api-current-state","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"Salcombe","server":"40c50b05.cc8cc4","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.salcombe_tide","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":380,"y":480,"wires":[["dbe5ac0b1cae2708"]]},{"id":"dbe5ac0b1cae2708","type":"function","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"Get today Tides","func":"var offset = Math.abs(new Date().getTimezoneOffset()) / 60;\nvar newArray;\nvar number = 0;\nfor (var i = 0; i < 6; i++) {\n var newMsg = {};\n newMsg.payload = {\n daytime: msg.data.attributes.predictions[i][0],\n stamp: Date.parse(msg.data.attributes.predictions[i][0]) + offset,\n nowstamp: Date.now() + offset,\n height: msg.data.attributes.predictions[i][1],\n number: i,\n\n }\n\n node.send(newMsg);\n}\nreturn null; ","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":480,"wires":[["4bb1dd113f0889f2"]]},{"id":"e6cb8fd0e3786138","type":"comment","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"","info":"Get the tide data as stored from the Rest api\n","x":380,"y":520,"wires":[]},{"id":"dfb5b1448f13f1eb","type":"comment","z":"6a77ed22f5291f7a","g":"87853723e6aabe04","name":"","info":"Get the tide data as stored from the Rest api\n","x":520,"y":520,"wires":[]},{"id":"fc1b6e026fc881e2","type":"ha-entity-config","server":"40c50b05.cc8cc4","deviceConfig":"","name":"tideclock2","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"tideclock2"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"40c50b05.cc8cc4","type":"server","name":"Home Assistant","addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":true,"heartbeatInterval":"30","statusSeparator":"","enableGlobalContextStore":false},{"id":"274078113daf8662","type":"group","z":"6a77ed22f5291f7a","name":"5 MINUTE TIDE HEIGHT CALC","style":{"label":true,"stroke":"#0070c0","label-position":"n","color":"#000000"},"nodes":["f8472beab0dae8ec","fa39edc95178c23d","b29dac6bfcb5cc6a"],"x":294,"y":619,"w":692,"h":82},{"id":"f8472beab0dae8ec","type":"function","z":"6a77ed22f5291f7a","g":"274078113daf8662","name":"function 53","func":"if (msg.payload.tidestate ==\"Rising\"){\n var next_tide = msg.payload.waterhigh\n var last_tide = msg.payload.waterlow\n} else {\n var next_tide = msg.payload.waterlow\n var last_tide = msg.payload.waterhigh \n}\n\n\nmsg.tide = {\n tidestate: msg.payload.tidestate,\n clockset: msg.payload.clockset,\n kwater: msg.payload.k_water,\n waterhigh: msg.payload.waterhigh,\n waterlow: msg.payload.waterlow,\n next_tide: next_tide,\n last_tide: last_tide,\n tideheight: msg.payload.TideHeight,\n hourselapsed: msg.payload.elapsed,\n event: msg.payload.event,\n} \nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":660,"wires":[["fa39edc95178c23d"]]},{"id":"fa39edc95178c23d","type":"function","z":"6a77ed22f5291f7a","g":"274078113daf8662","name":"TIDE HEIGHT CALCULATOR","func":"// REALTIME TIDE HEIGHTS FOR RISING AND FALLING TIDES WITH ANY TIDECYCLE USING THE RULE OF 12TH's\n// FREE TO USE WITH ACCREDITATION TO NORMAN DILLEY DARTWEB.CO.UK\n\n// VERSION 1.2 \n// Changelog: inserted an extra loop to correct error when elapsed time is less than the 1st period\n \n\n// THESE VARIABLES ARE AUTO EXTRACTED FROM TIDE DATA \nvar last_tideheight = msg.payload.LastTide;\nvar next_tideheight = msg.payload.NextTide;\n\nvar last_tidetime = msg.payload.LastTideTime;\nvar next_tidetime = msg.payload.NextTideTime;\n\n//INITIALISE -Required variables\nvar offset = Math.abs(new Date().getTimezoneOffset()) / 60; //local time offset\nvar nowstamp = Date.now() + offset; //apply offset to time now\n\n// IF AUTO EXTRACTED TIMES AND HEIGHTS ARE UNAVALABLE FILL THIS DATA \n//var last_tideheight = 4.9379256586396485; //metres\n//var next_tideheight = 1.1754144700349092; //metres\n\n//var last_tidetime = 1702892040000; //timestamp\n//var next_tidetime = 1702914600000; //timestamp\n\n//Convert to Timestamp if required\n//var last_tidetime1 = new Date(\"2023-12-17T08:45:00\"); //alternative\n//last_tidetime = last_tidetime1.getTime();\n//var nexttidetime1 = new Date(\"2023-12-17T15:01:00\"); //alternative\n//next_tidetime = nexttidetime1.getTime();\n\n//Timecycle\nvar tide_timecycle = (next_tidetime - last_tidetime)/(1000 * 3600); //hours\n\n//Tiderange\nif (next_tideheight > last_tideheight){\n var tide_state =\"Rising\"\n var tide_range = next_tideheight - last_tideheight\n}else{\n var tide_state =\"Falling\"\n var tide_range = last_tideheight - next_tideheight\n}\n\n//Tideflow divide cycle into 12th's - 6 parts (1,2,3,3,2,1)\nvar parts = [1,2,3,3,2,1];\nvar tideflow = [];\nfor (var i = 0; i < 6; i++) {\n tideflow.push(tide_range * (parts[i] / 12))\n}\n\n// Periods as lengths of an hour\nvar timesplit = tide_timecycle/6;\n\n//elapsed time in hours since tide event\nvar elapsedtime = nowstamp - last_tidetime\nelapsedtime = elapsedtime /(1000 * 3600); //hours;\n\n//Timespan adding periods\nvar periodadd = [1,2,3,4,5,6,7];\nvar period = [];\nfor (var i = 0; i < 7; i++) {\n period.push(timesplit * periodadd[i]) \n}\n\n//Match time period to actual time and calculate tideflow\nvar selectperiod;\nvar periodthis;\nvar i = 0;\nvar addon = 0;\n\nif (elapsedtime < period[0]){\n tideflow[0] \n selectperiod = period[0]\n periodthis = elapsedtime\n addon = 0\n var selecttide=tideflow[0]\n}else{\n\nwhile (elapsedtime > period[i]){\n i++;\n selectperiod = period[i]\n periodthis = elapsedtime - period[i-1] \n addon = tideflow[i-1] + addon\n var selecttide=tideflow[i]\n}\n}\n \n// Do the final Tide calculation realtime inc adjustment for Estuary \nvar tideheight;\nvar k_water;\nif (tide_state ==\"Falling\"){\n tideheight = last_tideheight - (addon + (selecttide * periodthis))\n k_water = tideheight - 1.06\n tideheight = Number(tideheight).toFixed(2)\n k_water = Number(k_water).toFixed(2) \n}else{\n tideheight = last_tideheight + addon + (selecttide * periodthis)\n k_water = tideheight - 1.06\n tideheight = Number(tideheight).toFixed(2)\n k_water = Number(k_water).toFixed(2) \n}\n\nmsg.payload = {\n tidestate: tide_state,\n tideheight: tideheight,\n k_water: k_water,\n nowstamp: nowstamp,\n next_tideheight,\n last_tideheight,\n}\n\nmsg.tide = {\n tidestate: tide_state,\n tiderange: tide_range,\n lasttime: last_tidetime,\n nexttime: next_tidetime,\n nowstamp: nowstamp,\n tidecycle: tide_timecycle,\n tideflow: tideflow,\n timesplit: timesplit,\n elapsedtime: elapsedtime,\n period: period,\n periodthis: periodthis,\n selectperiod: selectperiod,\n tideheight: tideheight,\n k_water: k_water,\n addon: addon,\n\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":650,"y":660,"wires":[["b29dac6bfcb5cc6a"]]},{"id":"b29dac6bfcb5cc6a","type":"debug","z":"6a77ed22f5291f7a","g":"274078113daf8662","name":"Output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":890,"y":660,"wires":[]}]
Iām using the noaa_tides also but have a little different attributes and includes tide_factor which provides the % of tide already so I have in my template:
- next_tide_time
- last_tide_time
- next_tide_type
- last_tide_type
- high_tide_level
- tide_factor
template:
- sensor:
- name: "tide pos"
unit_of_measurement: "%"
state: >
{{ state_attr('sensor.tide', 'tide_factor') }}
This is awesome but Iāve got a problem:
Iām using MetService for tides in NZ and it gives me 2 sensors:
sensor.lost_paradise_next_high_tide And sensor.lost_paradise_next_low_tide
In this format: May 19, 2024 at 4:01 AM
Iāve replaced these in the template sensor but it only comes back as āunavailableā itās probably got to do with the date/time format, but my Yama is too limited to know what to change in the template sensor.
Anyone got any ideas?
Dan
You need to try the template in dev tools. Report back.
Iām typing to use this but itās wonky (ok sometimes) with noaa_tides. The root issue I believe is that it really needs to be working off the ācurrentā state but it only shows the time and if itās high or low tide as a string also with no date and itās sometimes in that last or in the future within some number of hours. I wish I could better access the data. It also has a future low and high tide attributes that Iām using like in the scripts above for noaa and some times of the day it works OK with those numbers but sometimes not because the dates are too far off in the future I think. Are other noaa_tide users having the same issue?
Iāve included a sample of the date here:
And here is an odd graph (notice the jump UP in the late afternoon to the left.)
And the dial is just wrong now (it was 4:55PM when I snapped this.) I much farther away from low tide than shown.
Can you share the noaa_tide configuration (and your station you are using?) Iām just very surprised that itās different. Iād love to just use the factor rather than deal with the issue you can see in my prior post.
Just want to say, as the originator of this card, that I have always acknowledged that the timing is not completely accurate.
I use World Tides, and donāt seem to have the problems you describe with jumping around.
I assumed itās related to the noaa_tides integration and how exposes its data. I assume the World Tides is not free, am I wrong?
I do love your garage!
OK, I have an answer. There is a fork of the builtin nota_tides that has better, more usable info including the tide factor. It also shows last and next tide rather than next low and net high tides.
This is it:
And now I get a tide factor directly and different attribute info like this:
Using this data this seems to work well for tide_pos state to make sure the rising falling. The comment earlier in the thread to just use the factor as-is is not correct.
- sensor:
- name: tide_pos
unit_of_measurement: "%"
state: >
{% set last = state_attr('sensor.santa_cruz_tides', 'last_tide_type') %}
{% set factor = state_attr('sensor.santa_cruz_tides', 'tide_factor') %}
{% if (last == "High") > 0 %}
{{ 50 - (factor * 0.5)}}
{% else %}
{{ 50 + (factor * 0.5)}}
{%endif %}
Here is what the graph data looks like: (0%-50% is tide falling : 50%-100% is tide rising)
The guage is now very accurate with respect to the tide level, the āhourā guides really are not that accurate but you canāt have it both ways, since a tide cycle is not 6 hours and tide rising/lowering is not linear.
Here is my final tide section on my weather page:
By the way, world tides doesnāt cost me, there is a free tier.
Thanks, that was very unclear reading the site which is why I did not consider. I just updated my post above with my new tide pos calculation with the newer noaa_tides that I referenced. Thanks again.
Does anyone else think it may be useful to re-orient the gauge so that Rising was at the 12 oāclock position, with High at 3 oāclock. Then youād have Falling at 6 oāclock and Low at 9 oāclock. This way visually, anything above the āhorizonā would be a rising or incoming tide and anything below the horizon would be the falling or outgoing tide. Not sure how Iād need to change the sensor templates to fit that orientation. Any advice would be appreciated.
Nice idea. I might have a play.