that looks awesome, could you please share the code when you are ready
Its not really ready yet, just playing, but just put worldtides into my HA to get some real data
OK I think I have done it, but there hasn’t been a full tide cycle yet to check it all. There may be bugs!
I used the canvas gauge card which is available thru HACS. I set up a circular gauge with zero at 12 o’clock. It does from 0 (12 o’clock) to 100 (also 12 o/clock). 12 o’clock represents high tide and 6 o’clock represents low tide.
I used the world tides integration as the tide sensor. It produces one entity with a state like “High Tide at 5:30 pm” and some attributes as follows:
attribution: Data provided by WorldTides
high_tide_time_utc: 2021-09-16T12:51+0000
high_tide_height: 0.853
low_tide_time_utc: 2021-09-16T19:01+0000
low_tide_height: -0.957
friendly_name: Lyttelton Harbour Tides
I then made a template sensor which (hopefully) gives a number between 0 and 100 representing the tide state. Between 0 and 50 the tide is falling and between 50 and 100 the tide is rising. The gauge simply displays that sensor. The sensor name is sensor.tide_pos
(short for position)
The gauge setup is as follows:
type: custom:canvas-gauge-card
entity: sensor.tide_pos
card_height: 250
gauge:
animation: false
type: radial-gauge
title: Tide
width: 220
height: 220
borderShadowWidth: 0
borderOuterWidth: 0
borderMiddleWidth: 0
borderInnerWidth: 0
minValue: 0
maxValue: 100
startAngle: 180
ticksAngle: 360
valueBox: false
majorTicks:
- High
- Falling
- Low
- Rising
- High
minorTicks: 2
strokeTicks: true
borders: true
highlights: []
The template sensor is as follows:
template:
- sensor:
- name: "tide pos"
unit_of_measurement: "%"
state: >
{% set high = as_timestamp(state_attr('sensor.lyttelton_harbour_tides', 'high_tide_time_utc'))|float %}
{% set low = as_timestamp(state_attr('sensor.lyttelton_harbour_tides', 'low_tide_time_utc'))|float %}
{% if (high - low) > 0 %}
{{50-(((low - as_timestamp(now()))/(high -low)) * 50)}}
{% else %}
{{100 - (((high - as_timestamp(now()))/(low-high)) * 50)}}
{%endif %}
It’s late and I can’t be bothered explaining the details tonight, but if anyone has questions, let me know.
thats great thank you very much, I have added the times to the side for now but i want to try and add that to the display, hopefully its as easy as adding the sensor to the “tick”
I enjoyed it, learned a bit about templating and time conversions.
I haven’t figured out how to put sensor text onto the gauge.
I changed this a little, as below
type: custom:canvas-gauge-card
entity: sensor.tide_pos
card_height: 250
gauge:
animation: false
type: radial-gauge
title: Tide
width: 220
height: 220
borderShadowWidth: 0
borderOuterWidth: 0
borderMiddleWidth: 0
borderInnerWidth: 0
minValue: 0
maxValue: 100
startAngle: 180
ticksAngle: 360
valueBox: false
majorTicks:
- High
- 1/5
- 2/4
- Falling
- 4/2
- 5/1
- Low
- 1/5
- 2/4
- Rising
- 4/2
- 5/1
- High
minorTicks: 2
strokeTicks: true
borders: true
highlights: []
colorNeedle: rgb(0,255,0)
useMinPath: true
as a result it looks like
The numbers mean, say at the four clock position “four hours from high tide, two hours until low”. Now this is only an approximation as the period between high and low tide is obviously not exactly six hours, but I find it rough enough.
This is exactly what I wanted to represent the tides. Also, informed me of terrific package available for gauges for many other purposes (temperatures, wind direction…). I used the NOAA tide data from Home Assistant NOAA Tides. Those attributes and your template produced the “position” between 0 and 100 perfect. Many thanks.
Hi,
Could you tell me exactly where you place this “template sensor”? Is it in a file in a folder, and if so, what’s the folder name, path, and exact filename you used? I thought it might be in configuration.yaml but that seems like it would make configuration.yaml a huge dumping ground for random code. Perhaps it’s in a file in a folder somewhere?
All I can find regarding HA templates relates to in-line code in automations. I found something regarding creating jinja files but you don’t mention anything about that.
Please try to be exact - I’m struggling with reading posts in here where only generalities are mentioned!
Many thanks
By default, the template sensors are defined in configuration.yaml, in the config directory.
Yes, this can become a mess as you add more and more configurations. That’s why it can be split up into smaller files which you then include. For example, here are my configuration.yaml entries which bring in two separate files which define my template sensors:
template:
- binary_sensor: !include template_binary_sensors.yaml
- sensor: !include template_sensors.yaml
The two files, template_sensors.yaml and template_binary_sensors.yaml, are both at the same level in the config directory.
It took me a bit of searching, but I found where in the documentation this is explained:
I have only just spotted your article, which was very helpful and gave me a good starting point and i just wanted to say thanks.
Having got this up and running I decided to add a breakdown of the water height at minute intervals, which is helpful if you live on an estuary.
Then of course I realised that this method is not strictly accurate as you are not calculating the current postion of the tide, but the next cycle, which can have a lower or higher, high/low tide. If you are calculating tides around the Isle of Wight/Poole UK area, this will probably be less accurate as they have 2 high tides per day.
Your method is very simple and it’s not far out, but I decided to re-work this in Node-Red as it became a little complicated. If anyone wants my Node-Red flow, give me a shout.
I do accept the inaccuracies. It is quite tidal here but all I wanted was a quick indication of roughly where the cycle is. Of course I can also look out the window.
Glad my work helped, and glad that you improved it. Cheers.
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":[]}]