Instant Ink Countdown for Hp Printer Integration

great job, thanks!

Hi
I would like to user your integration but I can’t install xml-twig-tools. Home assistant is installed via docker and the command pip install xml-twig-tools give me these errors :

ERROR: Could not find a version that satisfies the requirement xml-twig-tools (from versions: none)
ERROR: No matching distribution found for xml-twig-tools

Can you help me with the package installation ?
Thanks

Finally, I installed the perl-xml-twig package and it seems to work.
Now I have errors when I execute the automation.

HP Envy 6230 Page Reset: Error executing script. Invalid data for call_service at pos 2: expected float for dictionary value @ data['value']

In the logs : Command failed: date -d '2020-10-25 1 month' +%Y-%m-%d

@EventuallyFixed Can you make a last post with all the code please ?
I grab code from multiple post so maybe I forgot something

Thanks a lot

hi @elyisum , i didn’t use “perl-xml-twig”, but hp official integration of “home assistant”.

for the reset automation error I can’t help you, this is mine and it works:

- alias: HP Envy 6230 Reset Mese
  trigger:
    - platform: time
      at: '00:30:00'
  condition:
    - condition: template
      value_template: "{{ now().day == 25 }}"
  action:
  - service: input_number.set_value
    entity_id: input_number.hp_envy_6230_pages_rollover_allowance
    data_template:
      value: "{% if states('sensor.hp_envy_6230_pages_allowance_remaining')|int +\
        \ states('sensor.hp_envy_6230_pages_rollover_remaining')|int > states('input_number.hp_envy_6230_pages_rollover_monthly_max_allowance')|int\
        \ %}\n  {{ states('input_number.hp_envy_6230_pages_rollover_monthly_max_allowance')|int\
        \ }}\n{% else %}\n  {{ states('sensor.hp_envy_6230_pages_allowance_remaining')|int\
        \ + states('sensor.hp_envy_6230_pages_rollover_remaining')|int }}\n{% endif\
        \ %}"
  - service: input_datetime.set_datetime
    entity_id: input_datetime.hp_envy_6230_this_period_start_date
    data_template:
      date: '{{ states(''sensor.date'') }}'
  - service: input_number.set_value
    entity_id: input_number.hp_envy_6230_pages_at_month_start
    data_template:
      value: '{{ states(''sensor.hp_envy_6230_total_pages_printed'') }}'

Thanks for your reply
I installed perl-xml-twig to get the Totalimpression from the XML
I tried your code : no erros but there are no improvements. Sensors values are always 0.
Can you post all the sensors please ?

Here is my configuration…

Inputs

# Inputs for the HP Ink Print Plan
input_number:
# The total number of pages that had been printed
# at the start of this period of the printing plan
# Set by an automation
  hp_envy_5540_pages_at_month_start:
    name: Printed Pages Total at month start
    min: 0
    max: 999999
    step: 1
    icon: mdi:printer
    mode: box
# The current monthly allowance number of pages
# Set manually
  hp_envy_5540_pages_monthly_allowance:
    name: Monthly Allowance Pages
    min: 0
    max: 999999
    step: 1
    icon: mdi:printer
    mode: box
# The maximum number of rollover pages allowed.
# Ten on the free tier, 2x pages on the paid tier.
# Set manually
  hp_envy_5540_pages_rollover_monthly_max_allowance:
    name: Rollover Maximum Pages
    min: 0
    max: 999999
    step: 1
    icon: mdi:printer
    mode: box
# The rollover allowance for this month
# Set by an automation
  hp_envy_5540_pages_rollover_allowance:
    name: Rollover Allowance Pages
    min: 0
    max: 999999
    step: 1
    icon: mdi:printer
    mode: box
# The number of pages in each overprint block, when
# the Monthly Allowance plus Rollover are exceeded
# Set manually
  hp_envy_5540_pages_overprint_block_size:
    name: Over Print Block Size
    min: 0
    max: 999999
    step: 1
    icon: mdi:printer
    mode: box
# The cost of each overprint block, in £
# Set manually
  hp_envy_5540_pages_overprint_block_cost:
    name: Over Print Block Cost
    min: 0
    max: 999999
    step: 0.01
    icon: mdi:currency-gbp
    mode: box
# The start date of this period of the print plan
# Set by automation    
input_datetime:
  hp_envy_5540_this_period_start_date:
    name: This Period Start Date
    has_date: true
    has_time: false
    icon: mdi:calendar

Date/Time Sensor

- platform: time_date
  display_options:
    - 'time'
    - 'date'
    - 'date_time'
    - 'date_time_utc'
    - 'date_time_iso'
    - 'time_date'
    - 'time_utc'
    - 'beat'

Template Sensors

- platform: template
  sensors:
  
# Used to track days remaining of HP Ink Printer Pages period
# Needs to have the date sensors installed as per: https://www.home-assistant.io/integrations
# Use of sensor.date ought to force a daily update, according to https://www.home-assistant.io/integrations/template/
    hp_envy_5540_allowance_days_remaining:
      friendly_name: "Allowance Days Remaining"
      value_template: >-
         {{ ((as_timestamp(strptime(states('sensor.hp_envy_5540_next_renewal_date'), '%Y-%m-%d'))|int - as_timestamp(strptime(states('sensor.date'), '%Y-%m-%d'))|int ) / 86400)|int }}
#
# The number of allowance pages remaining for this period
# Calculated when each page is printed, and at the start of each new Instant Ink period
# "{% (if states('input_datetime.hp_envy_5540_this_period_start_date') %}" is a workaround to set a listener)
    hp_envy_5540_pages_allowance_remaining:
      friendly_name: "Allowance Pages Remaining"
      value_template: >-
        {% if states('input_datetime.hp_envy_5540_this_period_start_date') %}
          {% if states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int >= 0 %}
            {{ states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int }}
          {% else %}
            0
          {% endif %}
        {% endif %}
#
# The number of rollover pages remaining for this period.
# Calculated when each page is printed, when the Rollover Allowance is reset, and at the start of each new Instant Ink period
    hp_envy_5540_pages_rollover_remaining:
      friendly_name: "Rollover Pages Remaining"
      value_template: >-
        {% if states('input_datetime.hp_envy_5540_this_period_start_date') %}
          {% if states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int < 0 and states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int + states('input_number.hp_envy_5540_pages_rollover_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int >= 0 %}
            {{ states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int + states('input_number.hp_envy_5540_pages_rollover_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int }}
          {% elif states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int - states('sensor.hp_envy_5540_total_pages_printed')|int < 0 and states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int + states('input_number.hp_envy_5540_pages_rollover_allowance')|int -   states('sensor.hp_envy_5540_total_pages_printed')|int < 0 %}
            0
          {% elif 1==1 %}
            {{ states('input_number.hp_envy_5540_pages_rollover_allowance')|int }}
          {% endif %}
        {% endif %}
#
# The number pages printed over and above the Monthly Allowance plus the Rollover Allowance
# Calculated when each page is printed, and at the start of each new Instant Ink period
    hp_envy_5540_pages_overprint:
      friendly_name: "Over Allowance Prints"
      value_template: >-
        {% if states('input_datetime.hp_envy_5540_this_period_start_date') %}
          {% if states('input_number.hp_envy_5540_pages_at_month_start')|int + states('input_number.hp_envy_5540_pages_monthly_allowance')|int + states('input_number.hp_envy_5540_pages_rollover_allowance')|int < states('sensor.hp_envy_5540_total_pages_printed')|int %}
            {{ states('sensor.hp_envy_5540_total_pages_printed')|int - states('input_number.hp_envy_5540_pages_rollover_allowance')|int - states('input_number.hp_envy_5540_pages_monthly_allowance')|int - states('input_number.hp_envy_5540_pages_at_month_start')|int }}
          {% else %}
            0
          {% endif %}    
        {% endif %}
# The cost so far of the pages that have been printed over the Allowance plus Rollover
# Calculated when each page is printed, and at the start of each new Instant Ink period
    hp_envy_5540_pages_overprint_cost:
      friendly_name: "Over Allowance Cost"
      value_template: >-
        {% if states('input_datetime.hp_envy_5540_total_pages_printed') or states('input_datetime.hp_envy_5540_this_period_start_date') %}
          {% if states('sensor.hp_envy_5540_pages_overprint')|int == 0 %}
            0
          {% elif states('sensor.hp_envy_5540_pages_overprint')|int > 0 and ( (states('sensor.hp_envy_5540_pages_overprint')|int - 1) % states('input_number.hp_envy_5540_pages_overprint_block_size')|int == 0 ) %}
            {{ (1 + ((states('sensor.hp_envy_5540_pages_overprint')|int - 1) / states('input_number.hp_envy_5540_pages_overprint_block_size')|int)) | multiply( states('input_number.hp_envy_5540_pages_overprint_block_cost')|int)|int  }}
          {% else %}
            {{ (1 + ((states('sensor.hp_envy_5540_pages_overprint')|int - 1) / states('input_number.hp_envy_5540_pages_overprint_block_size')|int)) | multiply( states('input_number.hp_envy_5540_pages_overprint_block_cost')|int)|int  }}
          {% endif %}    
        {% endif %}    
      unit_of_measurement: '£'

    hp_envy_5540_pages_overprint_remaining:
      friendly_name: "Over Allowance Prints Remaining"
      value_template: >-
        {% if states('sensor.hp_envy_5540_pages_overprint')|int == 0 or (states('sensor.hp_envy_5540_pages_overprint')|int % states('input_number.hp_envy_5540_pages_overprint_block_size')|int == 0) %}
          0
        {% else %}
          {{ states('input_number.hp_envy_5540_pages_overprint_block_size')|int - (states('sensor.hp_envy_5540_pages_overprint')|int % states('input_number.hp_envy_5540_pages_overprint_block_size')|int) }}
        {% endif %}     
        
# EITHER use this, or the Command Line sensor below named the same:
    hp_envy_5540_next_renewal_date:
      friendly_name: HP Envy 5540 Next Renewal Date
      value_template: >-
        {% if (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1 > 12) %}
          {{ (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int + 1)|string + "-01-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
        {% else %}
          {{ states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int|string + "-" +  ('%02d' % (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1)) + "-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
        {% endif %}        

Scrape Sensors

# Sensor to get 'HP Envy 5540 Total Pages Printed' from the printer's web service.
# The whole project depends on getting this value.
# The tag, '<dd:TotalImpressions PEID="5082">' is used here, BUT THE TAG YOU NEED MAY BE DIFFERENT.
- platform: scrape
  resource: http://[Your_Printer_IP_Address]/DevMgmt/ProductUsageDyn.xml
  name: hp_envy_5540_total_pages_printed
  select: 'dd\:TotalImpressions[PEID="5082"]'
  value_template: >-
    {% if value == "" %}
      0
    {% else %}
      {{ value }}
    {% endif %}

Command Line Sensors
These are alternative sensors that may be tried and used, if the sensors above don’t work. You will need to comment/remove the sensors above, and uncomment these.

# Used with input_datetime, & input_number, to track the state of HP Printer Instant Ink

# This is a command line sensor alternative to the scrape sensor, hp_envy_5540_total_pages_printed, above
# It needs package, 'xml-twig-tools', to be installed in Raspbian
# Inspired by: https://community.home-assistant.io/t/command-line-sensor-with-a-value-template-struggling/125957/3
# The whole project depends on getting this value.
# The tag, '<dd:TotalImpressions PEID="5082">' is used here, BUT THE TAG YOU NEED MAY BE DIFFERENT.
# ------
# Gets the HP Printer Total Pages Printed from the printer's web service
#- platform: command_line
#  name: "HP Envy 5540 Total Pages Printed"
#  command: "xml_grep 'dd:TotalImpressions[@PEID=\"5082\"]' http://[Your_Printer_IP_Address]/DevMgmt/ProductUsageDyn.xml --text_only"
#  value_template: "{{ value }}"


# This is a command line sensor alternative to the template sensor, hp_envy_5540_next_renewal_date, above
# ------
# Gets the HP Ink Next Allowance Renewal Date, using the command line for the calculation
#- platform: command_line
#  name: "HP Envy 5540 Next Renewal Date"
#  command: "date -d '{{ states('input_datetime.hp_envy_5540_this_period_start_date') }} 1 month' +%Y-%m-%d"
#  value_template: "{{ value }}"

Automation

- id: '1588925532080'
  alias: HP Envy 5540 Page Reset
  description: 'Sets: - Total number of pages printed at the start of the period -
    Start Date of this period (to store a date to calculate the next one) - Maximum
    Rollover Allowance value for this month - Rollover Remaining to new Maximum Rollover
    Allowance value for this month - Rollover from overprint pages'
  trigger:
  - at: 00:00:00
    platform: time
  condition:
  - condition: template
    value_template: '{{ now().day|int == states(''input_datetime.hp_envy_5540_this_period_start_date'')[-2:]|int
      }}'
  action:
  - service: input_number.set_value
    entity_id: input_number.hp_envy_5540_pages_rollover_allowance
    data_template:
      value: ' {% if states(''sensor.hp_envy_5540_pages_overprint'')|int > 0 and (states(''sensor.hp_envy_5540_pages_overprint'')|int
        % states(''input_number.hp_envy_5540_pages_overprint_block_size'')|int) >
        0 %} {{ states(''input_number.hp_envy_5540_pages_overprint_block_size'')|int
        - (states(''sensor.hp_envy_5540_pages_overprint'')|int % states(''input_number.hp_envy_5540_pages_overprint_block_size'')|int)  }}
        {% elif states(''sensor.hp_envy_5540_pages_allowance_remaining'')|int + states(''sensor.hp_envy_5540_pages_rollover_remaining'')|int
        > states(''input_number.hp_envy_5540_pages_rollover_monthly_max_allowance'')|int
        %} {{ states(''input_number.hp_envy_5540_pages_rollover_monthly_max_allowance'')|int
        }} {% else %} {{ states(''sensor.hp_envy_5540_pages_allowance_remaining'')|int
        + states(''sensor.hp_envy_5540_pages_rollover_remaining'')|int }} {% endif
        %}'
  - service: input_number.set_value
    entity_id: input_number.hp_envy_5540_pages_at_month_start
    data_template:
      value: '{{ states(''sensor.hp_envy_5540_total_pages_printed'') }}'
  - service: input_datetime.set_datetime
    entity_id: input_datetime.hp_envy_5540_this_period_start_date
    data_template:
      date: '{{ states(''sensor.date'') }}'

Lovelace Cards

Configuration

cards:
  - entities:
      - entity: input_datetime.hp_envy_5540_this_period_start_date
      - entity: input_number.hp_envy_5540_pages_at_month_start
      - entity: sensor.hp_envy_5540_total_pages_printed
      - entity: input_number.hp_envy_5540_pages_monthly_allowance
      - entity: input_number.hp_envy_5540_pages_rollover_allowance
      - entity: input_number.hp_envy_5540_pages_rollover_monthly_max_allowance
      - entity: input_number.hp_envy_5540_pages_overprint_block_size
      - entity: input_number.hp_envy_5540_pages_overprint_block_cost
    title: HP Envy Instant Ink Configuration
    type: entities
type: vertical-stack

Sensors

cards:
  - entities:
      - entity: sensor.hp_envy_5540_allowance_days_remaining
      - entity: sensor.hp_envy_5540_pages_allowance_remaining
      - entity: sensor.hp_envy_5540_pages_rollover_remaining
      - entity: sensor.hp_envy_5540_pages_overprint
      - entity: sensor.hp_envy_5540_pages_overprint_cost
      - entity: sensor.hp_envy_5540_pages_overprint_remaining
    title: HP Envy Instant Ink
    type: entities
  - cards:
# These twp are using the IPP Integration
      - entity: sensor.hp_envy_5540_series_black_ink
        max: 100
        min: 0
        name: Black Ink
        type: gauge
      - entity: sensor.hp_envy_5540_series_tri_color_ink
        max: 100
        min: 0
        name: Tri-Colour Ink
        type: gauge
    type: horizontal-stack
type: vertical-stack
1 Like

In the XML from the printer, I have a tag

<dd:TotalImpressions PEID="5082">1234</dd:TotalImpressions>

so the xml_grep command here is looking for that tag (dd:TotalImpressions) with the attribute, PEID=“5082”.

  • Do you have that tag with that attribute?
  • When you drop to the OS and run the same command, do you get an output? If so, what?
xml_grep 'dd:TotalImpressions[@PEID="5082"]' http://[Your_Printer_IP_Address]/DevMgmt/ProductUsageDyn.xml --text_only

Hi @EventuallyFixed
Thanks for your help. I pasted all your code

I have the tag in the xml file : <dd:TotalImpressions PEID=“5082”>424</dd:TotalImpressions>

Here is the result of the command line xml_grep


The value appears twice. Is it normal ?

When I launch the automation, this error appears

Command failed: date -d '2020-10-25 1 month' +%Y-%m-%d

For the xml_grep: Are there two tags with ‘<dd:TotalImpressions PEID=“5082”>’? If so, that is why there are two answers. Do you have a unique tag & attribute in the document that can be used, one which increments every time a page is printed? If so use that.
If not you could try something like adding | head -qn1, or | awk 'NR==1{print $1}’ to take only the first line of the output:

xml_grep 'dd:TotalImpressions[@PEID="5082"]' http://[Your_Printer_IP_Address]/DevMgmt/ProductUsageDyn.xml --text_only | head -qn1

or

xml_grep 'dd:TotalImpressions[@PEID="5082"]' http://[Your_Printer_IP_Address]/DevMgmt/ProductUsageDyn.xml --text_only | awk 'NR==1{print $1}'

For the date: Your OS is not doing the date calculation. Please tell me what OS and shell are you using? For the shell, drop to the OS and give the output from this:

echo "$SHELL"

Also, the result of this could be helpful:

ls /bin/bash

To get the date part working, it would be easier for you to use the Template Sensor instead of the command line sensor. So change this:

#    hp_envy_5540_next_renewal_date:
#      friendly_name: HP Envy 5540 Next Renewal Date
#      value_template: >-
#        {% if (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1 > 12) %}
#          {{ (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int + 1)|string + "-01-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
#        {% else %}
#          {{ states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int|string + "-" +  ('%02d' % (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1)) + "-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
#        {% endif %}

to this, uncommenting the code by removing the ‘#’ at the start of the line:

    hp_envy_5540_next_renewal_date:
      friendly_name: HP Envy 5540 Next Renewal Date
      value_template: >-
        {% if (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1 > 12) %}
          {{ (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int + 1)|string + "-01-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
        {% else %}
          {{ states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[0]|int|string + "-" +  ('%02d' % (states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[1]|int + 1)) + "-" + states('input_datetime.hp_envy_5540_this_period_start_date').split("-")[2] }}
        {% endif %}

and then change this:

- platform: command_line
  name: "HP Envy 5540 Next Renewal Date"
  command: "date -d '{{ states('input_datetime.hp_envy_5540_this_period_start_date') }} 1 month' +%Y-%m-%d"
  value_template: "{{ value }}"

to this, commenting-out the code by adding a ‘#’ at the start of the line

#- platform: command_line
#  name: "HP Envy 5540 Next Renewal Date"
#  command: "date -d '{{ states('input_datetime.hp_envy_5540_this_period_start_date') }} 1 month' +%Y-%m-%d"
#  value_template: "{{ value }}"

Thanks for all your help !

echo "$SHELL"and ls /bin/bash

gives me /bin/bash

I’m confused, why I need to comment the xml_grep command line to get the date part working ?

I’m confused, why I need to comment the xml_grep command line to get the date part working ?

It’s me that is confused! :smile: I’ve corrected the code substitution above.

What is your OS? Is it HassOS? Raspbian/Raspbery Pi OS? Some other OS? I ask because it’s OS differences that are causing the command-line date sensor to not work. I’m using Raspbian, which by default uses the bash shell.

Hi,
I have tried as well for 2 days to get your solution running. Unfortunately I am not familiar linux/unix, so I guess I belong -meanwhile - to the majority using HassOS simple installation via image on SD card.
As soon as there is a code working with HassOS, I might try it again. But I do now better stick to the initial solution without page carryover.

Finally, it seems to work without errors :slight_smile:
I still have to configure according to my subscription.
I have a last (I hope) question : why “Printed Pages Total at month start” has a minimal value to 500 ?

hi, I have hassos and this code works for me:

    hp_envy_6230_next_renewal_date:
      friendly_name: "HP Envy 6230 Prossima Data Rinnovo"
      value_template: >-
        {% if (states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[1]|int + 1 > 12) %}
          {{  (states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[0]|int + 1)|string + "-01-" + states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[2]  }}
        {% else %}
          {{  states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[0]|int|string + "-" + (states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[1]|int + 1)|string + "-" + states('input_datetime.hp_envy_6230_this_period_start_date').split("-")[2]  }}
        {% endif %} 

and if you don’t have it yet, created this sensor:

- platform: time_date
  display_options:
    - 'time'
    - 'date'

No reason other than I had just over 500 printed pages at the time ot was created. You can change that.

1 Like

For completeness, I have amended the configuration as follows:

  • Added the date/time sensor, as it’s a prerequisite (thanks @acuplush)
  • New Scrape sensor to get the total number of pages printed from printer’s web service.
    • This means that the configuration uses only Home Assistant objects, and removes any Operating System dependencies.

hello, since it is now a bug-free code, would it be feasible to create a package? so as to have everything in order and easy to understand for other users.

1 Like

Good idea. However I’d have to learn how to do that, and for the foreseeable future my time is rather limited.

1 Like

Hi @EventuallyFixed
Where can I find the new configuration to gest the total number of pages printed with HA objects ?
Thanks

It’s all in the post above.