Skip to content

Instantly share code, notes, and snippets.

@epaulsen
Created October 2, 2025 17:35
Show Gist options
  • Select an option

  • Save epaulsen/9dcae3a7ad17a127ea35efd92ec410fa to your computer and use it in GitHub Desktop.

Select an option

Save epaulsen/9dcae3a7ad17a127ea35efd92ec410fa to your computer and use it in GitHub Desktop.
Hack for å få timespris ut fra nordpool-sensoren.
mqtt:
sensor:
- name: "Nordpool Spot Timespris Kr.Sand"
unique_id: "sensor.nordpool_spot_timespris_krsand"
state_topic: "Nordpool/Spot_Timespris/value"
unit_of_measurement: "NOK/kWh"
value_template: "{{ value_json | float(0) }}"
json_attributes_topic: "Nordpool/Spot_Timespris/attributes"
[{"id":"c7e13d35b37b1a11","type":"inject","z":"c47a770c26e3be71","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":80,"y":1560,"wires":[["be9c0199d1412355"]]},{"id":"be9c0199d1412355","type":"api-current-state","z":"c47a770c26e3be71","name":"","server":"72b5aa9aa9eb914e","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.nordpool_kwh_no2_nok_3_095_025","state_type":"str","blockInputOverrides":true,"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":410,"y":1560,"wires":[["ad7f9e243bd10930","a51ddd79662f2acf"]]},{"id":"a1a407acab9df560","type":"server-state-changed","z":"c47a770c26e3be71","name":"","server":"72b5aa9aa9eb914e","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["sensor.nordpool_kwh_no2_nok_3_095_025"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":400,"y":1620,"wires":[["ad7f9e243bd10930","a51ddd79662f2acf"]]},{"id":"ad7f9e243bd10930","type":"function","z":"c47a770c26e3be71","name":"Parse timepris","func":"function calculateHourlyAverages(inputArray) {\n const hourlyMap = {};\n\n inputArray.forEach(entry => {\n const start = new Date(entry.start);\n const hourKey = new Date(\n start.getFullYear(),\n start.getMonth(),\n start.getDate(),\n start.getHours(),\n 0, 0\n ).getTime(); // Bruk timestamp som nøkkel\n if (!hourlyMap[hourKey]) {\n hourlyMap[hourKey] = [];\n }\n hourlyMap[hourKey].push(entry.value);\n });\n\n const toIsoWithOffset = (date) => {\n const pad = (n) => n.toString().padStart(2, '0');\n const yyyy = date.getFullYear();\n const mm = pad(date.getMonth() + 1);\n const dd = pad(date.getDate());\n const hh = pad(date.getHours());\n const min = pad(date.getMinutes());\n const ss = pad(date.getSeconds());\n const offset = -date.getTimezoneOffset(); // i minutter\n const sign = offset >= 0 ? '+' : '-';\n const offsetHours = pad(Math.floor(Math.abs(offset) / 60));\n const offsetMinutes = pad(Math.abs(offset) % 60);\n return `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}${sign}${offsetHours}:${offsetMinutes}`;\n };\n\n const result = Object.entries(hourlyMap).map(([timestamp, values]) => {\n const startDate = new Date(parseInt(timestamp));\n const endDate = new Date(startDate.getTime() + 60 * 60 * 1000);\n const avg = values.reduce((sum, val) => sum + val, 0) / values.length;\n\n return {\n start: toIsoWithOffset(startDate),\n end: toIsoWithOffset(endDate),\n value: Math.round(avg * 1000) / 1000\n };\n });\n\n return result;\n}\n\nfunction calculateCurrentHourAverage(inputArray) {\n const now = new Date();\n const currentHourStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), 0, 0);\n const currentHourEnd = new Date(currentHourStart.getTime() + 60 * 60 * 1000);\n\n const currentHourValues = inputArray.filter(entry => {\n const start = new Date(entry.start);\n return start >= currentHourStart && start < currentHourEnd;\n }).map(entry => entry.value);\n\n if (currentHourValues.length === 0) {\n return null;\n }\n\n const avg = currentHourValues.reduce((sum, val) => sum + val, 0) / currentHourValues.length;\n return Math.round(avg * 1000) / 1000;\n}\n\nconst raw_today = calculateHourlyAverages(msg.data.attributes.raw_today);\nconst raw_tomorrow = calculateHourlyAverages(msg.data.attributes.raw_tomorrow);\n\nconst currentValue = calculateCurrentHourAverage(msg.data.attributes.raw_today);\n\n// var payload = \n// {\n// currentValue, // TODO: Beregn snitt for alle verdier i timen vi er inne i,\n// \"attributes\": {\n// \"raw_today\" : raw_today,\n// \"raw_tomorrow\": raw_tomorrow\n// }\n// };\n\nmsg.payload = currentValue;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":1580,"wires":[["521719d0f1642c01"]]},{"id":"a51ddd79662f2acf","type":"function","z":"c47a770c26e3be71","name":"Parse attributes","func":"function calculateHourlyAverages(inputArray) {\n const hourlyMap = {};\n\n inputArray.forEach(entry => {\n const start = new Date(entry.start);\n const hourKey = new Date(\n start.getFullYear(),\n start.getMonth(),\n start.getDate(),\n start.getHours(),\n 0, 0\n ).getTime(); // Bruk timestamp som nøkkel\n if (!hourlyMap[hourKey]) {\n hourlyMap[hourKey] = [];\n }\n hourlyMap[hourKey].push(entry.value);\n });\n\n const toIsoWithOffset = (date) => {\n const pad = (n) => n.toString().padStart(2, '0');\n const yyyy = date.getFullYear();\n const mm = pad(date.getMonth() + 1);\n const dd = pad(date.getDate());\n const hh = pad(date.getHours());\n const min = pad(date.getMinutes());\n const ss = pad(date.getSeconds());\n const offset = -date.getTimezoneOffset(); // i minutter\n const sign = offset >= 0 ? '+' : '-';\n const offsetHours = pad(Math.floor(Math.abs(offset) / 60));\n const offsetMinutes = pad(Math.abs(offset) % 60);\n return `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}${sign}${offsetHours}:${offsetMinutes}`;\n };\n\n const result = Object.entries(hourlyMap).map(([timestamp, values]) => {\n const startDate = new Date(parseInt(timestamp));\n const endDate = new Date(startDate.getTime() + 60 * 60 * 1000);\n const avg = values.reduce((sum, val) => sum + val, 0) / values.length;\n\n return {\n start: toIsoWithOffset(startDate),\n end: toIsoWithOffset(endDate),\n value: Math.round(avg * 1000) / 1000\n };\n });\n\n return result;\n}\n\nconst raw_today = calculateHourlyAverages(msg.data.attributes.raw_today);\nconst raw_tomorrow = calculateHourlyAverages(msg.data.attributes.raw_tomorrow);\n\n\nvar payload = \n{ \n \"attributes\": {\n \"raw_today\" : raw_today,\n \"raw_tomorrow\": raw_tomorrow\n }\n};\n\nmsg.payload = payload;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":820,"y":1620,"wires":[["2d4835965d52cc02"]]},{"id":"521719d0f1642c01","type":"mqtt out","z":"c47a770c26e3be71","name":"Publish Timepris","topic":"Nordpool/Spot_Timespris/value","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"210b8811ec1781ab","x":1160,"y":1560,"wires":[]},{"id":"2d4835965d52cc02","type":"mqtt out","z":"c47a770c26e3be71","name":"Publish Timepris","topic":"Nordpool/Spot_Timespris/attributes","qos":"1","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"210b8811ec1781ab","x":1160,"y":1640,"wires":[]},{"id":"72b5aa9aa9eb914e","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":true,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":": ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true},{"id":"210b8811ec1781ab","type":"mqtt-broker","name":"z2m","broker":"192.168.1.226","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]
@epaulsen
Copy link
Author

epaulsen commented Oct 2, 2025

image

Fremgangsmåte:

  1. Importer node-red.json inn i node-red via copy-paste. Oppdater input-sensorer (de blå med din egen nordpool-sensor. Deploy til node-red.
  2. Legg yaml-biten inn der hvor du har andre mqtt-sensorer, eller rett i configuration.yaml dersom du ikke har noen andre mqtt-entiteter.
    Tilpass på sensoren til hva enn du ønsker.
  3. Developer Tools -> Yaml -> Reload MQTT entities

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment