How to convert ISO duration to seconds?

tasmota used to return seconds for the uptime. recently the format was changed to iso duration, this is what the tasmota telemetry returns:

{"Time":"2018-06-11T05:19:06","Uptime":"0T22:30:52","Vcc":3.218,"POWER":"OFF","Wifi":{"AP":1,"SSId":"xxx","RSSI":80,"APMac":"34:31:xx:xx:xx:xx"}}

i had hoped i could use a template like this, but it seems the iso8601_to_time filter is not available to the jinja template engine in HA.

{{ "1T22:30:00"|iso8601_to_time }}

any idea how to make the tasmota uptime usable in a sensor (again)?

You could convert it to timestamps and then calculate the diff. below is just an example

{% set uptime = as_timestamp(“2018-06-11T05:19:06”) %} # insert your variable here
{% set date = as_timestamp(now()) %} # get the current timestamp of the date
{%set diff = date - start_date %} # calculate diff in seconds
{{ diff }} # print uptime in seconds

edit: for more complete explanation

thanks, but uptime is not a date, but a duration. therefore as_timestamp("0T22:30:52") does not work with duration.

Is there answer for this?

Hello

Anyone struggling with this, I have written code to convert the duration into number of seconds elapsed.

There may be better or easier ways to do this, but it works at least so allows me to make use of the uptime that Tasmota returns.

{% set duration = state_attr("sensor.light_bathroom_2_status", "Uptime") %}

{% set days = duration.split('T')[0] | int %}

{% set hrs_mins_secs = duration.split('T')[1] %}

{% set hrs = hrs_mins_secs.split(':')[0] | int %}
{% set mins = hrs_mins_secs.split(':')[1] | int %}
{% set secs = hrs_mins_secs.split(':')[2] | int %}

{% set seconds_elapsed = (days * 60 * 60 * 24) + (hrs * 60 * 60) + (mins * 60) + secs %}

{{ seconds_elapsed }}

1 Like

This is an old thread but though I’d share how I went about converting a ISO8601 Duration string, e.g “P1DT2H5M34S” or “PT5M0S”, or even “PT0S” when null.

My use case is the PSA API for my EV returns charging remaining time until full/threshold reached in that format but wanted a more helpful display in HA.

Python is not my strength, so may be a better way around it:

# Get duration in ISO8601.
{%- set duration = state_attr("sensor.zafi", "energy")[0]["charging"]["remaining_time"] -%}
# Remove the P prefix and split into days and time.
{%- set duration = duration.replace('P','').split('T') -%}
# Only set Days if present in duration.
{%- set days = duration[0].split('D')[0] if 'D' in duration[0] else '' -%}
{%- set time = duration[1] -%}
# Get hours, minutes & seconds if set, otherwise empty string.
# Update variable at each stage, removing already extracted incremement.
{%- set hrs = time.split('H')[0] if 'H' in time else '' -%}
{%- set time = time.split('H')[1] if 'H' in time else time -%}
# Minutes
{%- set mins = time.split('M')[0] if 'M' in time else '' -%}
{%- set time = time.split('M')[1] if 'M' in time else time -%}
# Seconds
{%- set secs = time.split('S')[0] if time.split('S')[0]|int > 0 else '' -%}
# Create list to iterate over and generate human readable string value.
{%- set values = [('day', days),('hour', hrs),('minute', mins), ('second', secs)] -%}
{%- set remaining = namespace(time=[]) -%}
{%- for increment,value in values -%}
  # check for empty string (duration incremement not available)
  {%- set value = value | int if value else 0 -%}
  {%- if value > 0 -%}
    # Human readable string: "3 hours 1 minute" (with pluralization).
    {%- set value = value ~ ' ' ~ increment ~ 's' if value > 1 else value ~ ' ' ~ increment -%}
    # Add available time increments to list.
    {%- set remaining.time = remaining.time + [value] -%}
  {%- endif -%}
{%- endfor -%}
{{- remaining.time|join(' ') -}}

There’s a shorter way: the as_timedelta function can convert ISO8601 strings to Python timedelta objects, which you can then format at will.

Exact replica of your template (I think) with no looping:

{% set td = state_attr('sensor.zafi', 'energy')[0]['charging']['remaining_time']|as_timedelta %}
{% set d, s = td.days, td.seconds %}
{% set h, s = s//3600, s%3600 %}
{% set m, s = s//60, s%60 %}
{{ [("","%d day%s"%(d,("","s")[d>1]))[d>0],
    ("","%d hour%s"%(h,("","s")[h>1]))[h>0],
    ("","%d minute%s"%(m,("","s")[m>1]))[m>0],
    ("","%d second%s"%(s,("","s")[s>1]))[s>0]]
   |reject('eq','')|join(' ') }}
3 Likes

I’m glad I posted my solution, as in fact I’m happy with just using the |as_timedelta filter so thanks for that, brilliant!

1 Like